• 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(), ref_count(0), track_with_no_reference_count(0) {}
23 
VarInfo(Var * v,int input_ref_count)24 VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
25     : var(v), ref_count(input_ref_count), track_with_no_reference_count(0) {}
26 
VarTracker(ThreadMode thread_mode)27 VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
28   if (thread_mode == SINGLE_THREADED)
29     thread_checker_.reset(new base::ThreadChecker);
30 }
31 
~VarTracker()32 VarTracker::~VarTracker() {}
33 
CheckThreadingPreconditions() const34 void VarTracker::CheckThreadingPreconditions() const {
35   DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
36 #ifndef NDEBUG
37   ProxyLock::AssertAcquired();
38 #endif
39 }
40 
AddVar(Var * var)41 int32 VarTracker::AddVar(Var* var) {
42   CheckThreadingPreconditions();
43 
44   return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
45 }
46 
GetVar(int32 var_id) const47 Var* VarTracker::GetVar(int32 var_id) const {
48   CheckThreadingPreconditions();
49 
50   VarMap::const_iterator result = live_vars_.find(var_id);
51   if (result == live_vars_.end())
52     return NULL;
53   return result->second.var.get();
54 }
55 
GetVar(const PP_Var & var) const56 Var* VarTracker::GetVar(const PP_Var& var) const {
57   CheckThreadingPreconditions();
58 
59   if (!IsVarTypeRefcounted(var.type))
60     return NULL;
61   return GetVar(static_cast<int32>(var.value.as_id));
62 }
63 
AddRefVar(int32 var_id)64 bool VarTracker::AddRefVar(int32 var_id) {
65   CheckThreadingPreconditions();
66 
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.
72     return false;
73   }
74 
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);
80 
81     TrackedObjectGettingOneRef(found);
82   }
83 
84   // Basic refcount increment.
85   info.ref_count++;
86   return true;
87 }
88 
AddRefVar(const PP_Var & var)89 bool VarTracker::AddRefVar(const PP_Var& var) {
90   CheckThreadingPreconditions();
91 
92   if (!IsVarTypeRefcounted(var.type))
93     return true;
94   return AddRefVar(static_cast<int32>(var.value.as_id));
95 }
96 
ReleaseVar(int32 var_id)97 bool VarTracker::ReleaseVar(int32 var_id) {
98   CheckThreadingPreconditions();
99 
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())
104     return false;
105 
106   VarInfo& info = found->second;
107   if (info.ref_count == 0) {
108     NOTREACHED() << "Releasing an object with zero ref";
109     return false;
110   }
111   info.ref_count--;
112 
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);
122     } else {
123       // All other var types can just be released.
124       DCHECK(info.track_with_no_reference_count == 0);
125       var->ResetVarID();
126       live_vars_.erase(found);
127     }
128   }
129   return true;
130 }
131 
ReleaseVar(const PP_Var & var)132 bool VarTracker::ReleaseVar(const PP_Var& var) {
133   CheckThreadingPreconditions();
134 
135   if (!IsVarTypeRefcounted(var.type))
136     return false;
137   return ReleaseVar(static_cast<int32>(var.value.as_id));
138 }
139 
AddVarInternal(Var * var,AddVarRefMode mode)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)
143     return 0;
144 
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);
151 
152   return new_id;
153 }
154 
GetLiveVar(int32 id)155 VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
156   return live_vars_.find(id);
157 }
158 
GetRefCountForObject(const PP_Var & plugin_object)159 int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) {
160   CheckThreadingPreconditions();
161 
162   VarMap::iterator found = GetLiveVar(plugin_object);
163   if (found == live_vars_.end())
164     return -1;
165   return found->second.ref_count;
166 }
167 
GetTrackedWithNoReferenceCountForObject(const PP_Var & plugin_object)168 int VarTracker::GetTrackedWithNoReferenceCountForObject(
169     const PP_Var& plugin_object) {
170   CheckThreadingPreconditions();
171 
172   VarMap::iterator found = GetLiveVar(plugin_object);
173   if (found == live_vars_.end())
174     return -1;
175   return found->second.track_with_no_reference_count;
176 }
177 
178 // static
IsVarTypeRefcounted(PP_VarType type)179 bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
180   return type >= PP_VARTYPE_STRING;
181 }
182 
GetLiveVar(const PP_Var & var)183 VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
184   return live_vars_.find(static_cast<int32>(var.value.as_id));
185 }
186 
GetLiveVar(const PP_Var & var) const187 VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(const PP_Var& var)
188     const {
189   return live_vars_.find(static_cast<int32>(var.value.as_id));
190 }
191 
MakeArrayBufferPPVar(uint32 size_in_bytes)192 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes) {
193   CheckThreadingPreconditions();
194 
195   scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes));
196   if (!array_buffer.get())
197     return PP_MakeNull();
198   return array_buffer->GetPPVar();
199 }
200 
MakeArrayBufferPPVar(uint32 size_in_bytes,const void * data)201 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
202                                         const void* data) {
203   CheckThreadingPreconditions();
204 
205   ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data);
206   return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull();
207 }
208 
MakeArrayBufferVar(uint32 size_in_bytes,const void * data)209 ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes,
210                                                const void* data) {
211   CheckThreadingPreconditions();
212 
213   ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes));
214   if (!array_buffer)
215     return NULL;
216   memcpy(array_buffer->Map(), data, size_in_bytes);
217   return array_buffer;
218 }
219 
MakeArrayBufferPPVar(uint32 size_in_bytes,base::SharedMemoryHandle handle)220 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
221                                         base::SharedMemoryHandle handle) {
222   CheckThreadingPreconditions();
223 
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();
229 }
230 
MakeResourcePPVar(PP_Resource pp_resource)231 PP_Var VarTracker::MakeResourcePPVar(PP_Resource pp_resource) {
232   CheckThreadingPreconditions();
233 
234   ResourceVar* resource_var = MakeResourceVar(pp_resource);
235   return resource_var ? resource_var->GetPPVar() : PP_MakeNull();
236 }
237 
GetLiveVars()238 std::vector<PP_Var> VarTracker::GetLiveVars() {
239   CheckThreadingPreconditions();
240 
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();
245        ++iter) {
246     var_vector.push_back(iter->second.var->GetPPVar());
247   }
248   return var_vector;
249 }
250 
TrackedObjectGettingOneRef(VarMap::const_iterator obj)251 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
252   // Anybody using tracked objects should override this.
253   NOTREACHED();
254 }
255 
ObjectGettingZeroRef(VarMap::iterator iter)256 void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
257   DeleteObjectInfoIfNecessary(iter);
258 }
259 
DeleteObjectInfoIfNecessary(VarMap::iterator 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);
266   return true;
267 }
268 
269 }  // namespace ppapi
270