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()
22 : var(), ref_count(0), track_with_no_reference_count(0) {}
24 VarTracker::VarInfo::VarInfo(Var
* v
, int input_ref_count
)
25 : var(v
), ref_count(input_ref_count
), track_with_no_reference_count(0) {}
27 VarTracker::VarTracker(ThreadMode thread_mode
) : last_var_id_(0) {
28 if (thread_mode
== SINGLE_THREADED
)
29 thread_checker_
.reset(new base::ThreadChecker
);
32 VarTracker::~VarTracker() {}
34 void VarTracker::CheckThreadingPreconditions() const {
35 DCHECK(!thread_checker_
|| thread_checker_
->CalledOnValidThread());
37 ProxyLock::AssertAcquired();
41 int32
VarTracker::AddVar(Var
* var
) {
42 CheckThreadingPreconditions();
44 return AddVarInternal(var
, ADD_VAR_TAKE_ONE_REFERENCE
);
47 Var
* VarTracker::GetVar(int32 var_id
) const {
48 CheckThreadingPreconditions();
50 VarMap::const_iterator result
= live_vars_
.find(var_id
);
51 if (result
== live_vars_
.end())
53 return result
->second
.var
.get();
56 Var
* VarTracker::GetVar(const PP_Var
& var
) const {
57 CheckThreadingPreconditions();
59 if (!IsVarTypeRefcounted(var
.type
))
61 return GetVar(static_cast<int32
>(var
.value
.as_id
));
64 bool VarTracker::AddRefVar(int32 var_id
) {
65 CheckThreadingPreconditions();
67 DLOG_IF(ERROR
, !CheckIdType(var_id
, PP_ID_TYPE_VAR
))
68 << var_id
<< " is not a PP_Var ID.";
69 VarMap::iterator found
= live_vars_
.find(var_id
);
70 if (found
== live_vars_
.end()) {
71 NOTREACHED(); // Invalid var.
75 VarInfo
& info
= found
->second
;
76 if (info
.ref_count
== 0) {
77 // All live vars with no refcount should be tracked objects.
78 DCHECK(info
.track_with_no_reference_count
> 0);
79 DCHECK(info
.var
->GetType() == PP_VARTYPE_OBJECT
);
81 TrackedObjectGettingOneRef(found
);
84 // Basic refcount increment.
89 bool VarTracker::AddRefVar(const PP_Var
& var
) {
90 CheckThreadingPreconditions();
92 if (!IsVarTypeRefcounted(var
.type
))
94 return AddRefVar(static_cast<int32
>(var
.value
.as_id
));
97 bool VarTracker::ReleaseVar(int32 var_id
) {
98 CheckThreadingPreconditions();
100 DLOG_IF(ERROR
, !CheckIdType(var_id
, PP_ID_TYPE_VAR
))
101 << var_id
<< " is not a PP_Var ID.";
102 VarMap::iterator found
= live_vars_
.find(var_id
);
103 if (found
== live_vars_
.end())
106 VarInfo
& info
= found
->second
;
107 if (info
.ref_count
== 0) {
108 NOTREACHED() << "Releasing an object with zero ref";
113 if (info
.ref_count
== 0) {
114 // Hold a reference to the Var until it is erased so that we don't re-enter
115 // live_vars_.erase() during deletion.
116 // TODO(raymes): Make deletion of Vars iterative instead of recursive.
117 scoped_refptr
<Var
> var(info
.var
);
118 if (var
->GetType() == PP_VARTYPE_OBJECT
) {
119 // Objects have special requirements and may not necessarily be released
120 // when the refcount goes to 0.
121 ObjectGettingZeroRef(found
);
123 // All other var types can just be released.
124 DCHECK(info
.track_with_no_reference_count
== 0);
126 live_vars_
.erase(found
);
132 bool VarTracker::ReleaseVar(const PP_Var
& var
) {
133 CheckThreadingPreconditions();
135 if (!IsVarTypeRefcounted(var
.type
))
137 return ReleaseVar(static_cast<int32
>(var
.value
.as_id
));
140 int32
VarTracker::AddVarInternal(Var
* var
, AddVarRefMode mode
) {
141 // If the plugin manages to create millions of strings.
142 if (last_var_id_
== std::numeric_limits
<int32
>::max() >> kPPIdTypeBits
)
145 int32 new_id
= MakeTypedId(++last_var_id_
, PP_ID_TYPE_VAR
);
146 std::pair
<VarMap::iterator
, bool> was_inserted
=
147 live_vars_
.insert(std::make_pair(
148 new_id
, VarInfo(var
, mode
== ADD_VAR_TAKE_ONE_REFERENCE
? 1 : 0)));
149 // We should never insert an ID that already exists.
150 DCHECK(was_inserted
.second
);
155 VarTracker::VarMap::iterator
VarTracker::GetLiveVar(int32 id
) {
156 return live_vars_
.find(id
);
159 int VarTracker::GetRefCountForObject(const PP_Var
& plugin_object
) {
160 CheckThreadingPreconditions();
162 VarMap::iterator found
= GetLiveVar(plugin_object
);
163 if (found
== live_vars_
.end())
165 return found
->second
.ref_count
;
168 int VarTracker::GetTrackedWithNoReferenceCountForObject(
169 const PP_Var
& plugin_object
) {
170 CheckThreadingPreconditions();
172 VarMap::iterator found
= GetLiveVar(plugin_object
);
173 if (found
== live_vars_
.end())
175 return found
->second
.track_with_no_reference_count
;
179 bool VarTracker::IsVarTypeRefcounted(PP_VarType type
) {
180 return type
>= PP_VARTYPE_STRING
;
183 VarTracker::VarMap::iterator
VarTracker::GetLiveVar(const PP_Var
& var
) {
184 return live_vars_
.find(static_cast<int32
>(var
.value
.as_id
));
187 VarTracker::VarMap::const_iterator
VarTracker::GetLiveVar(const PP_Var
& var
)
189 return live_vars_
.find(static_cast<int32
>(var
.value
.as_id
));
192 PP_Var
VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes
) {
193 CheckThreadingPreconditions();
195 scoped_refptr
<ArrayBufferVar
> array_buffer(CreateArrayBuffer(size_in_bytes
));
196 if (!array_buffer
.get())
197 return PP_MakeNull();
198 return array_buffer
->GetPPVar();
201 PP_Var
VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes
,
203 CheckThreadingPreconditions();
205 ArrayBufferVar
* array_buffer
= MakeArrayBufferVar(size_in_bytes
, data
);
206 return array_buffer
? array_buffer
->GetPPVar() : PP_MakeNull();
209 ArrayBufferVar
* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes
,
211 CheckThreadingPreconditions();
213 ArrayBufferVar
* array_buffer(CreateArrayBuffer(size_in_bytes
));
216 memcpy(array_buffer
->Map(), data
, size_in_bytes
);
220 PP_Var
VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes
,
221 base::SharedMemoryHandle handle
) {
222 CheckThreadingPreconditions();
224 scoped_refptr
<ArrayBufferVar
> array_buffer(
225 CreateShmArrayBuffer(size_in_bytes
, handle
));
226 if (!array_buffer
.get())
227 return PP_MakeNull();
228 return array_buffer
->GetPPVar();
231 PP_Var
VarTracker::MakeResourcePPVar(PP_Resource pp_resource
) {
232 CheckThreadingPreconditions();
234 ResourceVar
* resource_var
= MakeResourceVar(pp_resource
);
235 return resource_var
? resource_var
->GetPPVar() : PP_MakeNull();
238 std::vector
<PP_Var
> VarTracker::GetLiveVars() {
239 CheckThreadingPreconditions();
241 std::vector
<PP_Var
> var_vector
;
242 var_vector
.reserve(live_vars_
.size());
243 for (VarMap::const_iterator iter
= live_vars_
.begin();
244 iter
!= live_vars_
.end();
246 var_vector
.push_back(iter
->second
.var
->GetPPVar());
251 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj
) {
252 // Anybody using tracked objects should override this.
256 void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter
) {
257 DeleteObjectInfoIfNecessary(iter
);
260 bool VarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter
) {
261 if (iter
->second
.ref_count
!= 0 ||
262 iter
->second
.track_with_no_reference_count
!= 0)
263 return false; // Object still alive.
264 iter
->second
.var
->ResetVarID();
265 live_vars_
.erase(iter
);