Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / ppapi / shared_impl / var_tracker.cc
blob771cde403f78032c25df9d6d50b314d1fbfb5ed9
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ppapi/shared_impl/var_tracker.h"
7 #include <string.h>
9 #include <limits>
11 #include "base/logging.h"
12 #include "base/shared_memory.h"
13 #include "ppapi/shared_impl/host_resource.h"
14 #include "ppapi/shared_impl/id_assignment.h"
15 #include "ppapi/shared_impl/proxy_lock.h"
16 #include "ppapi/shared_impl/var.h"
18 namespace ppapi {
20 VarTracker::VarInfo::VarInfo()
21 : var(),
22 ref_count(0),
23 track_with_no_reference_count(0) {
26 VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
27 : var(v),
28 ref_count(input_ref_count),
29 track_with_no_reference_count(0) {
32 VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
33 if (thread_mode == SINGLE_THREADED)
34 thread_checker_.reset(new base::ThreadChecker);
37 VarTracker::~VarTracker() {
40 void VarTracker::CheckThreadingPreconditions() const {
41 DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
42 #ifndef NDEBUG
43 ProxyLock::AssertAcquired();
44 #endif
47 int32 VarTracker::AddVar(Var* var) {
48 CheckThreadingPreconditions();
50 return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
53 Var* VarTracker::GetVar(int32 var_id) const {
54 CheckThreadingPreconditions();
56 VarMap::const_iterator result = live_vars_.find(var_id);
57 if (result == live_vars_.end())
58 return NULL;
59 return result->second.var.get();
62 Var* VarTracker::GetVar(const PP_Var& var) const {
63 CheckThreadingPreconditions();
65 if (!IsVarTypeRefcounted(var.type))
66 return NULL;
67 return GetVar(static_cast<int32>(var.value.as_id));
70 bool VarTracker::AddRefVar(int32 var_id) {
71 CheckThreadingPreconditions();
73 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
74 << var_id << " is not a PP_Var ID.";
75 VarMap::iterator found = live_vars_.find(var_id);
76 if (found == live_vars_.end()) {
77 NOTREACHED(); // Invalid var.
78 return false;
81 VarInfo& info = found->second;
82 if (info.ref_count == 0) {
83 // All live vars with no refcount should be tracked objects.
84 DCHECK(info.track_with_no_reference_count > 0);
85 DCHECK(info.var->GetType() == PP_VARTYPE_OBJECT);
87 TrackedObjectGettingOneRef(found);
90 // Basic refcount increment.
91 info.ref_count++;
92 return true;
95 bool VarTracker::AddRefVar(const PP_Var& var) {
96 CheckThreadingPreconditions();
98 if (!IsVarTypeRefcounted(var.type))
99 return true;
100 return AddRefVar(static_cast<int32>(var.value.as_id));
103 bool VarTracker::ReleaseVar(int32 var_id) {
104 CheckThreadingPreconditions();
106 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
107 << var_id << " is not a PP_Var ID.";
108 VarMap::iterator found = live_vars_.find(var_id);
109 if (found == live_vars_.end())
110 return false;
112 VarInfo& info = found->second;
113 if (info.ref_count == 0) {
114 NOTREACHED() << "Releasing an object with zero ref";
115 return false;
117 info.ref_count--;
119 if (info.ref_count == 0) {
120 // Hold a reference to the Var until it is erased so that we don't re-enter
121 // live_vars_.erase() during deletion.
122 // TODO(raymes): Make deletion of Vars iterative instead of recursive.
123 scoped_refptr<Var> var(info.var);
124 if (var->GetType() == PP_VARTYPE_OBJECT) {
125 // Objects have special requirements and may not necessarily be released
126 // when the refcount goes to 0.
127 ObjectGettingZeroRef(found);
128 } else {
129 // All other var types can just be released.
130 DCHECK(info.track_with_no_reference_count == 0);
131 var->ResetVarID();
132 live_vars_.erase(found);
135 return true;
138 bool VarTracker::ReleaseVar(const PP_Var& var) {
139 CheckThreadingPreconditions();
141 if (!IsVarTypeRefcounted(var.type))
142 return false;
143 return ReleaseVar(static_cast<int32>(var.value.as_id));
146 int32 VarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
147 // If the plugin manages to create millions of strings.
148 if (last_var_id_ == std::numeric_limits<int32>::max() >> kPPIdTypeBits)
149 return 0;
151 int32 new_id = MakeTypedId(++last_var_id_, PP_ID_TYPE_VAR);
152 std::pair<VarMap::iterator, bool> was_inserted =
153 live_vars_.insert(std::make_pair(new_id,
154 VarInfo(var, mode == ADD_VAR_TAKE_ONE_REFERENCE ? 1 : 0)));
155 // We should never insert an ID that already exists.
156 DCHECK(was_inserted.second);
158 return new_id;
161 VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
162 return live_vars_.find(id);
165 int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) {
166 CheckThreadingPreconditions();
168 VarMap::iterator found = GetLiveVar(plugin_object);
169 if (found == live_vars_.end())
170 return -1;
171 return found->second.ref_count;
174 int VarTracker::GetTrackedWithNoReferenceCountForObject(
175 const PP_Var& plugin_object) {
176 CheckThreadingPreconditions();
178 VarMap::iterator found = GetLiveVar(plugin_object);
179 if (found == live_vars_.end())
180 return -1;
181 return found->second.track_with_no_reference_count;
184 // static
185 bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
186 return type >= PP_VARTYPE_STRING;
189 VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
190 return live_vars_.find(static_cast<int32>(var.value.as_id));
193 VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(
194 const PP_Var& var) const {
195 return live_vars_.find(static_cast<int32>(var.value.as_id));
198 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes) {
199 CheckThreadingPreconditions();
201 scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes));
202 if (!array_buffer.get())
203 return PP_MakeNull();
204 return array_buffer->GetPPVar();
207 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
208 const void* data) {
209 CheckThreadingPreconditions();
211 ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data);
212 return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull();
215 ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes,
216 const void* data) {
217 CheckThreadingPreconditions();
219 ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes));
220 if (!array_buffer)
221 return NULL;
222 memcpy(array_buffer->Map(), data, size_in_bytes);
223 return array_buffer;
226 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
227 base::SharedMemoryHandle handle) {
228 CheckThreadingPreconditions();
230 scoped_refptr<ArrayBufferVar> array_buffer(
231 CreateShmArrayBuffer(size_in_bytes, handle));
232 if (!array_buffer.get())
233 return PP_MakeNull();
234 return array_buffer->GetPPVar();
237 std::vector<PP_Var> VarTracker::GetLiveVars() {
238 CheckThreadingPreconditions();
240 std::vector<PP_Var> var_vector;
241 var_vector.reserve(live_vars_.size());
242 for (VarMap::const_iterator iter = live_vars_.begin();
243 iter != live_vars_.end();
244 ++iter) {
245 var_vector.push_back(iter->second.var->GetPPVar());
247 return var_vector;
250 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
251 // Anybody using tracked objects should override this.
252 NOTREACHED();
255 void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
256 DeleteObjectInfoIfNecessary(iter);
259 bool VarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
260 if (iter->second.ref_count != 0 ||
261 iter->second.track_with_no_reference_count != 0)
262 return false; // Object still alive.
263 iter->second.var->ResetVarID();
264 live_vars_.erase(iter);
265 return true;
268 } // namespace ppapi