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/resource_tracker.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"
18 ResourceTracker::ResourceTracker(ThreadMode thread_mode
)
19 : last_resource_value_(0),
20 weak_ptr_factory_(this) {
21 if (thread_mode
== SINGLE_THREADED
)
22 thread_checker_
.reset(new base::ThreadChecker
);
25 ResourceTracker::~ResourceTracker() {
28 void ResourceTracker::CheckThreadingPreconditions() const {
29 DCHECK(!thread_checker_
|| thread_checker_
->CalledOnValidThread());
31 ProxyLock::AssertAcquired();
35 Resource
* ResourceTracker::GetResource(PP_Resource res
) const {
36 CheckThreadingPreconditions();
37 ResourceMap::const_iterator i
= live_resources_
.find(res
);
38 if (i
== live_resources_
.end())
40 return i
->second
.first
;
43 void ResourceTracker::AddRefResource(PP_Resource res
) {
44 CheckThreadingPreconditions();
45 DLOG_IF(ERROR
, !CheckIdType(res
, PP_ID_TYPE_RESOURCE
))
46 << res
<< " is not a PP_Resource.";
48 DCHECK(CanOperateOnResource(res
));
50 ResourceMap::iterator i
= live_resources_
.find(res
);
51 if (i
== live_resources_
.end())
54 // Prevent overflow of refcount.
55 if (i
->second
.second
==
56 std::numeric_limits
<ResourceAndRefCount::second_type
>::max())
59 // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
61 if (i
->second
.second
== 0)
62 i
->second
.first
->AddRef();
68 void ResourceTracker::ReleaseResource(PP_Resource res
) {
69 CheckThreadingPreconditions();
70 DLOG_IF(ERROR
, !CheckIdType(res
, PP_ID_TYPE_RESOURCE
))
71 << res
<< " is not a PP_Resource.";
73 DCHECK(CanOperateOnResource(res
));
75 ResourceMap::iterator i
= live_resources_
.find(res
);
76 if (i
== live_resources_
.end())
79 // Prevent underflow of refcount.
80 if (i
->second
.second
== 0)
84 if (i
->second
.second
== 0) {
85 LastPluginRefWasDeleted(i
->second
.first
);
87 // When we go from 1 to 0 plugin ref count, free the additional "real" ref
88 // on its behalf. THIS WILL MOST LIKELY RELEASE THE OBJECT AND REMOVE IT
90 i
->second
.first
->Release();
94 void ResourceTracker::ReleaseResourceSoon(PP_Resource res
) {
95 base::MessageLoop::current()->PostNonNestableTask(
97 RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource
,
98 weak_ptr_factory_
.GetWeakPtr(),
102 void ResourceTracker::DidCreateInstance(PP_Instance instance
) {
103 CheckThreadingPreconditions();
104 // Due to the infrastructure of some tests, the instance is registered
105 // twice in a few cases. It would be nice not to do that and assert here
107 if (instance_map_
.find(instance
) != instance_map_
.end())
109 instance_map_
[instance
] = linked_ptr
<InstanceData
>(new InstanceData
);
112 void ResourceTracker::DidDeleteInstance(PP_Instance instance
) {
113 CheckThreadingPreconditions();
114 InstanceMap::iterator found_instance
= instance_map_
.find(instance
);
116 // Due to the infrastructure of some tests, the instance is unregistered
117 // twice in a few cases. It would be nice not to do that and assert here
119 if (found_instance
== instance_map_
.end())
122 InstanceData
& data
= *found_instance
->second
;
124 // Force release all plugin references to resources associated with the
125 // deleted instance. Make a copy since as we iterate through them, each one
126 // will remove itself from the tracking info individually.
127 ResourceSet to_delete
= data
.resources
;
128 ResourceSet::iterator cur
= to_delete
.begin();
129 while (cur
!= to_delete
.end()) {
130 // Note that it's remotely possible for the object to already be deleted
131 // from the live resources. One case is if a resource object is holding
132 // the last ref to another. When we release the first one, it will release
133 // the second one. So the second one will be gone when we eventually get
135 ResourceMap::iterator found_resource
= live_resources_
.find(*cur
);
136 if (found_resource
!= live_resources_
.end()) {
137 Resource
* resource
= found_resource
->second
.first
;
138 if (found_resource
->second
.second
> 0) {
139 LastPluginRefWasDeleted(resource
);
140 found_resource
->second
.second
= 0;
142 // This will most likely delete the resource object and remove it
143 // from the live_resources_ list.
151 // In general the above pass will delete all the resources and there won't
152 // be any left in the map. However, if parts of the implementation are still
153 // holding on to internal refs, we need to tell them that the instance is
155 to_delete
= data
.resources
;
156 cur
= to_delete
.begin();
157 while (cur
!= to_delete
.end()) {
158 ResourceMap::iterator found_resource
= live_resources_
.find(*cur
);
159 if (found_resource
!= live_resources_
.end())
160 found_resource
->second
.first
->NotifyInstanceWasDeleted();
164 instance_map_
.erase(instance
);
167 int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance
) const {
168 CheckThreadingPreconditions();
169 InstanceMap::const_iterator found
= instance_map_
.find(instance
);
170 if (found
== instance_map_
.end())
172 return static_cast<int>(found
->second
->resources
.size());
175 void ResourceTracker::UseOddResourceValueInDebugMode() {
177 DCHECK_EQ(0, last_resource_value_
);
179 ++last_resource_value_
;
183 PP_Resource
ResourceTracker::AddResource(Resource
* object
) {
184 CheckThreadingPreconditions();
185 // If the plugin manages to create too many resources, don't do crazy stuff.
186 if (last_resource_value_
>= kMaxPPId
)
189 // Allocate an ID. Note there's a rare error condition below that means we
190 // could end up not using |new_id|, but that's harmless.
191 PP_Resource new_id
= MakeTypedId(GetNextResourceValue(), PP_ID_TYPE_RESOURCE
);
193 // Some objects have a 0 instance, meaning they aren't associated with any
194 // instance, so they won't be in |instance_map_|. This is (as of this writing)
195 // only true of the PPB_MessageLoop resource for the main thread.
196 if (object
->pp_instance()) {
197 InstanceMap::iterator found
= instance_map_
.find(object
->pp_instance());
198 if (found
== instance_map_
.end()) {
199 // If you hit this, it's likely somebody forgot to call DidCreateInstance,
200 // the resource was created with an invalid PP_Instance, or the renderer
201 // side tried to create a resource for a plugin that crashed/exited. This
202 // could happen for OOP plugins where due to reentrancies in context of
203 // outgoing sync calls the renderer can send events after a plugin has
205 DLOG(INFO
) << "Failed to find plugin instance in instance map";
208 found
->second
->resources
.insert(new_id
);
211 live_resources_
[new_id
] = ResourceAndRefCount(object
, 0);
215 void ResourceTracker::RemoveResource(Resource
* object
) {
216 CheckThreadingPreconditions();
217 PP_Resource pp_resource
= object
->pp_resource();
218 InstanceMap::iterator found
= instance_map_
.find(object
->pp_instance());
219 if (found
!= instance_map_
.end())
220 found
->second
->resources
.erase(pp_resource
);
221 live_resources_
.erase(pp_resource
);
224 void ResourceTracker::LastPluginRefWasDeleted(Resource
* object
) {
225 // Bug http://crbug.com/134611 indicates that sometimes the resource tracker
226 // is null here. This should never be the case since if we have a resource in
227 // the tracker, it should always have a valid instance associated with it
228 // (except for the resource for the main thread's message loop, which has
229 // instance set to 0).
230 // As a result, we do some CHECKs here to see what types of problems the
231 // instance might have before dispatching.
233 // TODO(brettw) remove these checks when this bug is no longer relevant.
234 // Note, we do an imperfect check here; this might be a loop that's not the
236 const bool is_message_loop
= (object
->AsPPB_MessageLoop_API() != NULL
);
237 CHECK(object
->pp_instance() || is_message_loop
);
238 CallbackTracker
* callback_tracker
=
239 PpapiGlobals::Get()->GetCallbackTrackerForInstance(object
->pp_instance());
240 CHECK(callback_tracker
|| is_message_loop
);
241 if (callback_tracker
)
242 callback_tracker
->PostAbortForResource(object
->pp_resource());
243 object
->NotifyLastPluginRefWasDeleted();
246 int32
ResourceTracker::GetNextResourceValue() {
248 return ++last_resource_value_
;
250 // In debug mode, the least significant bit indicates which side (renderer
251 // or plugin process) created the resource. Increment by 2 so it's always the
253 last_resource_value_
+= 2;
254 return last_resource_value_
;
258 bool ResourceTracker::CanOperateOnResource(PP_Resource res
) {
262 // The invalid PP_Resource value could appear at both sides.
266 // Skipping the type bits, the least significant bit of |res| should be the
267 // same as that of |last_resource_value_|.
268 return ((res
>> kPPIdTypeBits
) & 1) == (last_resource_value_
& 1);