• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ppapi/shared_impl/var_tracker.h"
6 
7 #include <string.h>
8 
9 #include <limits>
10 
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"
18 
19 namespace ppapi {
20 
VarInfo()21 VarTracker::VarInfo::VarInfo()
22     : var(),
23       ref_count(0),
24       track_with_no_reference_count(0) {
25 }
26 
VarInfo(Var * v,int input_ref_count)27 VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
28     : var(v),
29       ref_count(input_ref_count),
30       track_with_no_reference_count(0) {
31 }
32 
VarTracker(ThreadMode thread_mode)33 VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
34   if (thread_mode == SINGLE_THREADED)
35     thread_checker_.reset(new base::ThreadChecker);
36 }
37 
~VarTracker()38 VarTracker::~VarTracker() {
39 }
40 
CheckThreadingPreconditions() const41 void VarTracker::CheckThreadingPreconditions() const {
42   DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
43 #ifndef NDEBUG
44   ProxyLock::AssertAcquired();
45 #endif
46 }
47 
AddVar(Var * var)48 int32 VarTracker::AddVar(Var* var) {
49   CheckThreadingPreconditions();
50 
51   return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
52 }
53 
GetVar(int32 var_id) const54 Var* VarTracker::GetVar(int32 var_id) const {
55   CheckThreadingPreconditions();
56 
57   VarMap::const_iterator result = live_vars_.find(var_id);
58   if (result == live_vars_.end())
59     return NULL;
60   return result->second.var.get();
61 }
62 
GetVar(const PP_Var & var) const63 Var* VarTracker::GetVar(const PP_Var& var) const {
64   CheckThreadingPreconditions();
65 
66   if (!IsVarTypeRefcounted(var.type))
67     return NULL;
68   return GetVar(static_cast<int32>(var.value.as_id));
69 }
70 
AddRefVar(int32 var_id)71 bool VarTracker::AddRefVar(int32 var_id) {
72   CheckThreadingPreconditions();
73 
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.
79     return false;
80   }
81 
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);
87 
88     TrackedObjectGettingOneRef(found);
89   }
90 
91   // Basic refcount increment.
92   info.ref_count++;
93   return true;
94 }
95 
AddRefVar(const PP_Var & var)96 bool VarTracker::AddRefVar(const PP_Var& var) {
97   CheckThreadingPreconditions();
98 
99   if (!IsVarTypeRefcounted(var.type))
100     return true;
101   return AddRefVar(static_cast<int32>(var.value.as_id));
102 }
103 
ReleaseVar(int32 var_id)104 bool VarTracker::ReleaseVar(int32 var_id) {
105   CheckThreadingPreconditions();
106 
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())
111     return false;
112 
113   VarInfo& info = found->second;
114   if (info.ref_count == 0) {
115     NOTREACHED() << "Releasing an object with zero ref";
116     return false;
117   }
118   info.ref_count--;
119 
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);
129     } else {
130       // All other var types can just be released.
131       DCHECK(info.track_with_no_reference_count == 0);
132       var->ResetVarID();
133       live_vars_.erase(found);
134     }
135   }
136   return true;
137 }
138 
ReleaseVar(const PP_Var & var)139 bool VarTracker::ReleaseVar(const PP_Var& var) {
140   CheckThreadingPreconditions();
141 
142   if (!IsVarTypeRefcounted(var.type))
143     return false;
144   return ReleaseVar(static_cast<int32>(var.value.as_id));
145 }
146 
AddVarInternal(Var * var,AddVarRefMode mode)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)
150     return 0;
151 
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);
158 
159   return new_id;
160 }
161 
GetLiveVar(int32 id)162 VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
163   return live_vars_.find(id);
164 }
165 
GetRefCountForObject(const PP_Var & plugin_object)166 int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) {
167   CheckThreadingPreconditions();
168 
169   VarMap::iterator found = GetLiveVar(plugin_object);
170   if (found == live_vars_.end())
171     return -1;
172   return found->second.ref_count;
173 }
174 
GetTrackedWithNoReferenceCountForObject(const PP_Var & plugin_object)175 int VarTracker::GetTrackedWithNoReferenceCountForObject(
176     const PP_Var& plugin_object) {
177   CheckThreadingPreconditions();
178 
179   VarMap::iterator found = GetLiveVar(plugin_object);
180   if (found == live_vars_.end())
181     return -1;
182   return found->second.track_with_no_reference_count;
183 }
184 
185 // static
IsVarTypeRefcounted(PP_VarType type)186 bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
187   return type >= PP_VARTYPE_STRING;
188 }
189 
GetLiveVar(const PP_Var & var)190 VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
191   return live_vars_.find(static_cast<int32>(var.value.as_id));
192 }
193 
GetLiveVar(const PP_Var & var) const194 VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(
195     const PP_Var& var) const {
196   return live_vars_.find(static_cast<int32>(var.value.as_id));
197 }
198 
MakeArrayBufferPPVar(uint32 size_in_bytes)199 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes) {
200   CheckThreadingPreconditions();
201 
202   scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes));
203   if (!array_buffer.get())
204     return PP_MakeNull();
205   return array_buffer->GetPPVar();
206 }
207 
MakeArrayBufferPPVar(uint32 size_in_bytes,const void * data)208 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
209                                         const void* data) {
210   CheckThreadingPreconditions();
211 
212   ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data);
213   return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull();
214 }
215 
MakeArrayBufferVar(uint32 size_in_bytes,const void * data)216 ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes,
217                                                const void* data) {
218   CheckThreadingPreconditions();
219 
220   ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes));
221   if (!array_buffer)
222     return NULL;
223   memcpy(array_buffer->Map(), data, size_in_bytes);
224   return array_buffer;
225 }
226 
MakeArrayBufferPPVar(uint32 size_in_bytes,base::SharedMemoryHandle handle)227 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
228                                         base::SharedMemoryHandle handle) {
229   CheckThreadingPreconditions();
230 
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();
236 }
237 
MakeResourcePPVar(PP_Resource pp_resource)238 PP_Var VarTracker::MakeResourcePPVar(PP_Resource pp_resource) {
239   CheckThreadingPreconditions();
240 
241   ResourceVar* resource_var = MakeResourceVar(pp_resource);
242   return resource_var ? resource_var->GetPPVar() : PP_MakeNull();
243 }
244 
GetLiveVars()245 std::vector<PP_Var> VarTracker::GetLiveVars() {
246   CheckThreadingPreconditions();
247 
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();
252        ++iter) {
253     var_vector.push_back(iter->second.var->GetPPVar());
254   }
255   return var_vector;
256 }
257 
TrackedObjectGettingOneRef(VarMap::const_iterator obj)258 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
259   // Anybody using tracked objects should override this.
260   NOTREACHED();
261 }
262 
ObjectGettingZeroRef(VarMap::iterator iter)263 void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
264   DeleteObjectInfoIfNecessary(iter);
265 }
266 
DeleteObjectInfoIfNecessary(VarMap::iterator 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);
273   return true;
274 }
275 
276 }  // namespace ppapi
277