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/resource_tracker.h"
6
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/message_loop/message_loop.h"
10 #include "ppapi/shared_impl/callback_tracker.h"
11 #include "ppapi/shared_impl/id_assignment.h"
12 #include "ppapi/shared_impl/ppapi_globals.h"
13 #include "ppapi/shared_impl/proxy_lock.h"
14 #include "ppapi/shared_impl/resource.h"
15
16 namespace ppapi {
17
ResourceTracker(ThreadMode thread_mode)18 ResourceTracker::ResourceTracker(ThreadMode thread_mode)
19 : last_resource_value_(0), weak_ptr_factory_(this) {
20 if (thread_mode == SINGLE_THREADED)
21 thread_checker_.reset(new base::ThreadChecker);
22 }
23
~ResourceTracker()24 ResourceTracker::~ResourceTracker() {}
25
CheckThreadingPreconditions() const26 void ResourceTracker::CheckThreadingPreconditions() const {
27 DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
28 #ifndef NDEBUG
29 ProxyLock::AssertAcquired();
30 #endif
31 }
32
GetResource(PP_Resource res) const33 Resource* ResourceTracker::GetResource(PP_Resource res) const {
34 CheckThreadingPreconditions();
35 ResourceMap::const_iterator i = live_resources_.find(res);
36 if (i == live_resources_.end())
37 return NULL;
38 return i->second.first;
39 }
40
AddRefResource(PP_Resource res)41 void ResourceTracker::AddRefResource(PP_Resource res) {
42 CheckThreadingPreconditions();
43 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE))
44 << res << " is not a PP_Resource.";
45
46 DCHECK(CanOperateOnResource(res));
47
48 ResourceMap::iterator i = live_resources_.find(res);
49 if (i == live_resources_.end())
50 return;
51
52 // Prevent overflow of refcount.
53 if (i->second.second ==
54 std::numeric_limits<ResourceAndRefCount::second_type>::max())
55 return;
56
57 // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
58 // on its behalf.
59 if (i->second.second == 0)
60 i->second.first->AddRef();
61
62 i->second.second++;
63 return;
64 }
65
ReleaseResource(PP_Resource res)66 void ResourceTracker::ReleaseResource(PP_Resource res) {
67 CheckThreadingPreconditions();
68 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE))
69 << res << " is not a PP_Resource.";
70
71 DCHECK(CanOperateOnResource(res));
72
73 ResourceMap::iterator i = live_resources_.find(res);
74 if (i == live_resources_.end())
75 return;
76
77 // Prevent underflow of refcount.
78 if (i->second.second == 0)
79 return;
80
81 i->second.second--;
82 if (i->second.second == 0) {
83 LastPluginRefWasDeleted(i->second.first);
84
85 // When we go from 1 to 0 plugin ref count, free the additional "real" ref
86 // on its behalf. THIS WILL MOST LIKELY RELEASE THE OBJECT AND REMOVE IT
87 // FROM OUR LIST.
88 i->second.first->Release();
89 }
90 }
91
ReleaseResourceSoon(PP_Resource res)92 void ResourceTracker::ReleaseResourceSoon(PP_Resource res) {
93 base::MessageLoop::current()->PostNonNestableTask(
94 FROM_HERE,
95 RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource,
96 weak_ptr_factory_.GetWeakPtr(),
97 res)));
98 }
99
DidCreateInstance(PP_Instance instance)100 void ResourceTracker::DidCreateInstance(PP_Instance instance) {
101 CheckThreadingPreconditions();
102 // Due to the infrastructure of some tests, the instance is registered
103 // twice in a few cases. It would be nice not to do that and assert here
104 // instead.
105 if (instance_map_.find(instance) != instance_map_.end())
106 return;
107 instance_map_[instance] = linked_ptr<InstanceData>(new InstanceData);
108 }
109
DidDeleteInstance(PP_Instance instance)110 void ResourceTracker::DidDeleteInstance(PP_Instance instance) {
111 CheckThreadingPreconditions();
112 InstanceMap::iterator found_instance = instance_map_.find(instance);
113
114 // Due to the infrastructure of some tests, the instance is unregistered
115 // twice in a few cases. It would be nice not to do that and assert here
116 // instead.
117 if (found_instance == instance_map_.end())
118 return;
119
120 InstanceData& data = *found_instance->second;
121
122 // Force release all plugin references to resources associated with the
123 // deleted instance. Make a copy since as we iterate through them, each one
124 // will remove itself from the tracking info individually.
125 ResourceSet to_delete = data.resources;
126 ResourceSet::iterator cur = to_delete.begin();
127 while (cur != to_delete.end()) {
128 // Note that it's remotely possible for the object to already be deleted
129 // from the live resources. One case is if a resource object is holding
130 // the last ref to another. When we release the first one, it will release
131 // the second one. So the second one will be gone when we eventually get
132 // to it.
133 ResourceMap::iterator found_resource = live_resources_.find(*cur);
134 if (found_resource != live_resources_.end()) {
135 Resource* resource = found_resource->second.first;
136 if (found_resource->second.second > 0) {
137 LastPluginRefWasDeleted(resource);
138 found_resource->second.second = 0;
139
140 // This will most likely delete the resource object and remove it
141 // from the live_resources_ list.
142 resource->Release();
143 }
144 }
145
146 cur++;
147 }
148
149 // In general the above pass will delete all the resources and there won't
150 // be any left in the map. However, if parts of the implementation are still
151 // holding on to internal refs, we need to tell them that the instance is
152 // gone.
153 to_delete = data.resources;
154 cur = to_delete.begin();
155 while (cur != to_delete.end()) {
156 ResourceMap::iterator found_resource = live_resources_.find(*cur);
157 if (found_resource != live_resources_.end())
158 found_resource->second.first->NotifyInstanceWasDeleted();
159 cur++;
160 }
161
162 instance_map_.erase(instance);
163 }
164
GetLiveObjectsForInstance(PP_Instance instance) const165 int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance) const {
166 CheckThreadingPreconditions();
167 InstanceMap::const_iterator found = instance_map_.find(instance);
168 if (found == instance_map_.end())
169 return 0;
170 return static_cast<int>(found->second->resources.size());
171 }
172
UseOddResourceValueInDebugMode()173 void ResourceTracker::UseOddResourceValueInDebugMode() {
174 #if !defined(NDEBUG)
175 DCHECK_EQ(0, last_resource_value_);
176
177 ++last_resource_value_;
178 #endif
179 }
180
AddResource(Resource * object)181 PP_Resource ResourceTracker::AddResource(Resource* object) {
182 CheckThreadingPreconditions();
183 // If the plugin manages to create too many resources, don't do crazy stuff.
184 if (last_resource_value_ >= kMaxPPId)
185 return 0;
186
187 // Allocate an ID. Note there's a rare error condition below that means we
188 // could end up not using |new_id|, but that's harmless.
189 PP_Resource new_id = MakeTypedId(GetNextResourceValue(), PP_ID_TYPE_RESOURCE);
190
191 // Some objects have a 0 instance, meaning they aren't associated with any
192 // instance, so they won't be in |instance_map_|. This is (as of this writing)
193 // only true of the PPB_MessageLoop resource for the main thread.
194 if (object->pp_instance()) {
195 InstanceMap::iterator found = instance_map_.find(object->pp_instance());
196 if (found == instance_map_.end()) {
197 // If you hit this, it's likely somebody forgot to call DidCreateInstance,
198 // the resource was created with an invalid PP_Instance, or the renderer
199 // side tried to create a resource for a plugin that crashed/exited. This
200 // could happen for OOP plugins where due to reentrancies in context of
201 // outgoing sync calls the renderer can send events after a plugin has
202 // exited.
203 VLOG(1) << "Failed to find plugin instance in instance map";
204 return 0;
205 }
206 found->second->resources.insert(new_id);
207 }
208
209 live_resources_[new_id] = ResourceAndRefCount(object, 0);
210 return new_id;
211 }
212
RemoveResource(Resource * object)213 void ResourceTracker::RemoveResource(Resource* object) {
214 CheckThreadingPreconditions();
215 PP_Resource pp_resource = object->pp_resource();
216 InstanceMap::iterator found = instance_map_.find(object->pp_instance());
217 if (found != instance_map_.end())
218 found->second->resources.erase(pp_resource);
219 live_resources_.erase(pp_resource);
220 }
221
LastPluginRefWasDeleted(Resource * object)222 void ResourceTracker::LastPluginRefWasDeleted(Resource* object) {
223 // Bug http://crbug.com/134611 indicates that sometimes the resource tracker
224 // is null here. This should never be the case since if we have a resource in
225 // the tracker, it should always have a valid instance associated with it
226 // (except for the resource for the main thread's message loop, which has
227 // instance set to 0).
228 // As a result, we do some CHECKs here to see what types of problems the
229 // instance might have before dispatching.
230 //
231 // TODO(brettw) remove these checks when this bug is no longer relevant.
232 // Note, we do an imperfect check here; this might be a loop that's not the
233 // main one.
234 const bool is_message_loop = (object->AsPPB_MessageLoop_API() != NULL);
235 CHECK(object->pp_instance() || is_message_loop);
236 CallbackTracker* callback_tracker =
237 PpapiGlobals::Get()->GetCallbackTrackerForInstance(object->pp_instance());
238 CHECK(callback_tracker || is_message_loop);
239 if (callback_tracker)
240 callback_tracker->PostAbortForResource(object->pp_resource());
241 object->NotifyLastPluginRefWasDeleted();
242 }
243
GetNextResourceValue()244 int32 ResourceTracker::GetNextResourceValue() {
245 #if defined(NDEBUG)
246 return ++last_resource_value_;
247 #else
248 // In debug mode, the least significant bit indicates which side (renderer
249 // or plugin process) created the resource. Increment by 2 so it's always the
250 // same.
251 last_resource_value_ += 2;
252 return last_resource_value_;
253 #endif
254 }
255
CanOperateOnResource(PP_Resource res)256 bool ResourceTracker::CanOperateOnResource(PP_Resource res) {
257 #if defined(NDEBUG)
258 return true;
259 #else
260 // The invalid PP_Resource value could appear at both sides.
261 if (res == 0)
262 return true;
263
264 // Skipping the type bits, the least significant bit of |res| should be the
265 // same as that of |last_resource_value_|.
266 return ((res >> kPPIdTypeBits) & 1) == (last_resource_value_ & 1);
267 #endif
268 }
269
270 } // namespace ppapi
271