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"
11 #include "base/logging.h"
12 #include "base/memory/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/resource_var.h"
17 #include "ppapi/shared_impl/var.h"
21 VarTracker::VarInfo::VarInfo()
24 track_with_no_reference_count(0) {
27 VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
29 ref_count(input_ref_count),
30 track_with_no_reference_count(0) {
33 VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
34 if (thread_mode == SINGLE_THREADED)
35 thread_checker_.reset(new base::ThreadChecker);
38 VarTracker::~VarTracker() {
41 void VarTracker::CheckThreadingPreconditions() const {
42 DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
44 ProxyLock::AssertAcquired();
48 int32 VarTracker::AddVar(Var* var) {
49 CheckThreadingPreconditions();
51 return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
54 Var* VarTracker::GetVar(int32 var_id) const {
55 CheckThreadingPreconditions();
57 VarMap::const_iterator result = live_vars_.find(var_id);
58 if (result == live_vars_.end())
60 return result->second.var.get();
63 Var* VarTracker::GetVar(const PP_Var& var) const {
64 CheckThreadingPreconditions();
66 if (!IsVarTypeRefcounted(var.type))
68 return GetVar(static_cast<int32>(var.value.as_id));
71 bool VarTracker::AddRefVar(int32 var_id) {
72 CheckThreadingPreconditions();
74 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
75 << var_id << " is not a PP_Var ID.";
76 VarMap::iterator found = live_vars_.find(var_id);
77 if (found == live_vars_.end()) {
78 NOTREACHED(); // Invalid var.
82 VarInfo& info = found->second;
83 if (info.ref_count == 0) {
84 // All live vars with no refcount should be tracked objects.
85 DCHECK(info.track_with_no_reference_count > 0);
86 DCHECK(info.var->GetType() == PP_VARTYPE_OBJECT);
88 TrackedObjectGettingOneRef(found);
91 // Basic refcount increment.
96 bool VarTracker::AddRefVar(const PP_Var& var) {
97 CheckThreadingPreconditions();
99 if (!IsVarTypeRefcounted(var.type))
101 return AddRefVar(static_cast<int32>(var.value.as_id));
104 bool VarTracker::ReleaseVar(int32 var_id) {
105 CheckThreadingPreconditions();
107 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
108 << var_id << " is not a PP_Var ID.";
109 VarMap::iterator found = live_vars_.find(var_id);
110 if (found == live_vars_.end())
113 VarInfo& info = found->second;
114 if (info.ref_count == 0) {
115 NOTREACHED() << "Releasing an object with zero ref";
120 if (info.ref_count == 0) {
121 // Hold a reference to the Var until it is erased so that we don't re-enter
122 // live_vars_.erase() during deletion.
123 // TODO(raymes): Make deletion of Vars iterative instead of recursive.
124 scoped_refptr<Var> var(info.var);
125 if (var->GetType() == PP_VARTYPE_OBJECT) {
126 // Objects have special requirements and may not necessarily be released
127 // when the refcount goes to 0.
128 ObjectGettingZeroRef(found);
130 // All other var types can just be released.
131 DCHECK(info.track_with_no_reference_count == 0);
133 live_vars_.erase(found);
139 bool VarTracker::ReleaseVar(const PP_Var& var) {
140 CheckThreadingPreconditions();
142 if (!IsVarTypeRefcounted(var.type))
144 return ReleaseVar(static_cast<int32>(var.value.as_id));
147 int32 VarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
148 // If the plugin manages to create millions of strings.
149 if (last_var_id_ == std::numeric_limits<int32>::max() >> kPPIdTypeBits)
152 int32 new_id = MakeTypedId(++last_var_id_, PP_ID_TYPE_VAR);
153 std::pair<VarMap::iterator, bool> was_inserted =
154 live_vars_.insert(std::make_pair(new_id,
155 VarInfo(var, mode == ADD_VAR_TAKE_ONE_REFERENCE ? 1 : 0)));
156 // We should never insert an ID that already exists.
157 DCHECK(was_inserted.second);
162 VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
163 return live_vars_.find(id);
166 int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) {
167 CheckThreadingPreconditions();
169 VarMap::iterator found = GetLiveVar(plugin_object);
170 if (found == live_vars_.end())
172 return found->second.ref_count;
175 int VarTracker::GetTrackedWithNoReferenceCountForObject(
176 const PP_Var& plugin_object) {
177 CheckThreadingPreconditions();
179 VarMap::iterator found = GetLiveVar(plugin_object);
180 if (found == live_vars_.end())
182 return found->second.track_with_no_reference_count;
186 bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
187 return type >= PP_VARTYPE_STRING;
190 VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
191 return live_vars_.find(static_cast<int32>(var.value.as_id));
194 VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(
195 const PP_Var& var) const {
196 return live_vars_.find(static_cast<int32>(var.value.as_id));
199 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes) {
200 CheckThreadingPreconditions();
202 scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes));
203 if (!array_buffer.get())
204 return PP_MakeNull();
205 return array_buffer->GetPPVar();
208 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
210 CheckThreadingPreconditions();
212 ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data);
213 return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull();
216 ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes,
218 CheckThreadingPreconditions();
220 ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes));
223 memcpy(array_buffer->Map(), data, size_in_bytes);
227 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
228 base::SharedMemoryHandle handle) {
229 CheckThreadingPreconditions();
231 scoped_refptr<ArrayBufferVar> array_buffer(
232 CreateShmArrayBuffer(size_in_bytes, handle));
233 if (!array_buffer.get())
234 return PP_MakeNull();
235 return array_buffer->GetPPVar();
238 PP_Var VarTracker::MakeResourcePPVar(PP_Resource pp_resource) {
239 CheckThreadingPreconditions();
241 ResourceVar* resource_var = MakeResourceVar(pp_resource);
242 return resource_var ? resource_var->GetPPVar() : PP_MakeNull();
245 std::vector<PP_Var> VarTracker::GetLiveVars() {
246 CheckThreadingPreconditions();
248 std::vector<PP_Var> var_vector;
249 var_vector.reserve(live_vars_.size());
250 for (VarMap::const_iterator iter = live_vars_.begin();
251 iter != live_vars_.end();
253 var_vector.push_back(iter->second.var->GetPPVar());
258 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
259 // Anybody using tracked objects should override this.
263 void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
264 DeleteObjectInfoIfNecessary(iter);
267 bool VarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
268 if (iter->second.ref_count != 0 ||
269 iter->second.track_with_no_reference_count != 0)
270 return false; // Object still alive.
271 iter->second.var->ResetVarID();
272 live_vars_.erase(iter);