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/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"
20 VarTracker::VarInfo::VarInfo()
23 track_with_no_reference_count(0) {
26 VarTracker::VarInfo::VarInfo(Var
* v
, int input_ref_count
)
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());
43 ProxyLock::AssertAcquired();
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())
59 return result
->second
.var
.get();
62 Var
* VarTracker::GetVar(const PP_Var
& var
) const {
63 CheckThreadingPreconditions();
65 if (!IsVarTypeRefcounted(var
.type
))
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.
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.
95 bool VarTracker::AddRefVar(const PP_Var
& var
) {
96 CheckThreadingPreconditions();
98 if (!IsVarTypeRefcounted(var
.type
))
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())
112 VarInfo
& info
= found
->second
;
113 if (info
.ref_count
== 0) {
114 NOTREACHED() << "Releasing an object with zero ref";
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
);
129 // All other var types can just be released.
130 DCHECK(info
.track_with_no_reference_count
== 0);
132 live_vars_
.erase(found
);
138 bool VarTracker::ReleaseVar(const PP_Var
& var
) {
139 CheckThreadingPreconditions();
141 if (!IsVarTypeRefcounted(var
.type
))
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
)
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
);
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())
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())
181 return found
->second
.track_with_no_reference_count
;
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
,
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
,
217 CheckThreadingPreconditions();
219 ArrayBufferVar
* array_buffer(CreateArrayBuffer(size_in_bytes
));
222 memcpy(array_buffer
->Map(), data
, size_in_bytes
);
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();
245 var_vector
.push_back(iter
->second
.var
->GetPPVar());
250 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj
) {
251 // Anybody using tracked objects should override this.
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
);