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), weak_ptr_factory_(this) {
20 if (thread_mode
== SINGLE_THREADED
)
21 thread_checker_
.reset(new base::ThreadChecker
);
24 ResourceTracker::~ResourceTracker() {}
26 void ResourceTracker::CheckThreadingPreconditions() const {
27 DCHECK(!thread_checker_
|| thread_checker_
->CalledOnValidThread());
29 ProxyLock::AssertAcquired();
33 Resource
* ResourceTracker::GetResource(PP_Resource res
) const {
34 CheckThreadingPreconditions();
35 ResourceMap::const_iterator i
= live_resources_
.find(res
);
36 if (i
== live_resources_
.end())
38 return i
->second
.first
;
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.";
46 DCHECK(CanOperateOnResource(res
));
48 ResourceMap::iterator i
= live_resources_
.find(res
);
49 if (i
== live_resources_
.end())
52 // Prevent overflow of refcount.
53 if (i
->second
.second
==
54 std::numeric_limits
<ResourceAndRefCount::second_type
>::max())
57 // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
59 if (i
->second
.second
== 0)
60 i
->second
.first
->AddRef();
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.";
71 DCHECK(CanOperateOnResource(res
));
73 ResourceMap::iterator i
= live_resources_
.find(res
);
74 if (i
== live_resources_
.end())
77 // Prevent underflow of refcount.
78 if (i
->second
.second
== 0)
82 if (i
->second
.second
== 0) {
83 LastPluginRefWasDeleted(i
->second
.first
);
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
88 i
->second
.first
->Release();
92 void ResourceTracker::ReleaseResourceSoon(PP_Resource res
) {
93 PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostNonNestableTask(
95 RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource
,
96 weak_ptr_factory_
.GetWeakPtr(),
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
105 if (instance_map_
.find(instance
) != instance_map_
.end())
107 instance_map_
[instance
] = linked_ptr
<InstanceData
>(new InstanceData
);
110 void ResourceTracker::DidDeleteInstance(PP_Instance instance
) {
111 CheckThreadingPreconditions();
112 InstanceMap::iterator found_instance
= instance_map_
.find(instance
);
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
117 if (found_instance
== instance_map_
.end())
120 InstanceData
& data
= *found_instance
->second
;
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
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;
140 // This will most likely delete the resource object and remove it
141 // from the live_resources_ list.
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
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();
162 instance_map_
.erase(instance
);
165 int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance
) const {
166 CheckThreadingPreconditions();
167 InstanceMap::const_iterator found
= instance_map_
.find(instance
);
168 if (found
== instance_map_
.end())
170 return static_cast<int>(found
->second
->resources
.size());
173 void ResourceTracker::UseOddResourceValueInDebugMode() {
175 DCHECK_EQ(0, last_resource_value_
);
177 ++last_resource_value_
;
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
)
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
);
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
203 VLOG(1) << "Failed to find plugin instance in instance map";
206 found
->second
->resources
.insert(new_id
);
209 live_resources_
[new_id
] = ResourceAndRefCount(object
, 0);
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
);
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.
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
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();
244 int32
ResourceTracker::GetNextResourceValue() {
246 return ++last_resource_value_
;
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
251 last_resource_value_
+= 2;
252 return last_resource_value_
;
256 bool ResourceTracker::CanOperateOnResource(PP_Resource res
) {
260 // The invalid PP_Resource value could appear at both sides.
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);