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.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.";
47 ResourceMap::iterator i
= live_resources_
.find(res
);
48 if (i
== live_resources_
.end())
51 // Prevent overflow of refcount.
52 if (i
->second
.second
==
53 std::numeric_limits
<ResourceAndRefCount::second_type
>::max())
56 // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
58 if (i
->second
.second
== 0)
59 i
->second
.first
->AddRef();
65 void ResourceTracker::ReleaseResource(PP_Resource res
) {
66 CheckThreadingPreconditions();
67 DLOG_IF(ERROR
, !CheckIdType(res
, PP_ID_TYPE_RESOURCE
))
68 << res
<< " is not a PP_Resource.";
69 ResourceMap::iterator i
= live_resources_
.find(res
);
70 if (i
== live_resources_
.end())
73 // Prevent underflow of refcount.
74 if (i
->second
.second
== 0)
78 if (i
->second
.second
== 0) {
79 LastPluginRefWasDeleted(i
->second
.first
);
81 // When we go from 1 to 0 plugin ref count, free the additional "real" ref
82 // on its behalf. THIS WILL MOST LIKELY RELEASE THE OBJECT AND REMOVE IT
84 i
->second
.first
->Release();
88 void ResourceTracker::ReleaseResourceSoon(PP_Resource res
) {
89 base::MessageLoop::current()->PostNonNestableTask(
91 RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource
,
92 weak_ptr_factory_
.GetWeakPtr(),
96 void ResourceTracker::DidCreateInstance(PP_Instance instance
) {
97 CheckThreadingPreconditions();
98 // Due to the infrastructure of some tests, the instance is registered
99 // twice in a few cases. It would be nice not to do that and assert here
101 if (instance_map_
.find(instance
) != instance_map_
.end())
103 instance_map_
[instance
] = linked_ptr
<InstanceData
>(new InstanceData
);
106 void ResourceTracker::DidDeleteInstance(PP_Instance instance
) {
107 CheckThreadingPreconditions();
108 InstanceMap::iterator found_instance
= instance_map_
.find(instance
);
110 // Due to the infrastructure of some tests, the instance is unregistered
111 // twice in a few cases. It would be nice not to do that and assert here
113 if (found_instance
== instance_map_
.end())
116 InstanceData
& data
= *found_instance
->second
;
118 // Force release all plugin references to resources associated with the
119 // deleted instance. Make a copy since as we iterate through them, each one
120 // will remove itself from the tracking info individually.
121 ResourceSet to_delete
= data
.resources
;
122 ResourceSet::iterator cur
= to_delete
.begin();
123 while (cur
!= to_delete
.end()) {
124 // Note that it's remotely possible for the object to already be deleted
125 // from the live resources. One case is if a resource object is holding
126 // the last ref to another. When we release the first one, it will release
127 // the second one. So the second one will be gone when we eventually get
129 ResourceMap::iterator found_resource
= live_resources_
.find(*cur
);
130 if (found_resource
!= live_resources_
.end()) {
131 Resource
* resource
= found_resource
->second
.first
;
132 if (found_resource
->second
.second
> 0) {
133 LastPluginRefWasDeleted(resource
);
134 found_resource
->second
.second
= 0;
136 // This will most likely delete the resource object and remove it
137 // from the live_resources_ list.
145 // In general the above pass will delete all the resources and there won't
146 // be any left in the map. However, if parts of the implementation are still
147 // holding on to internal refs, we need to tell them that the instance is
149 to_delete
= data
.resources
;
150 cur
= to_delete
.begin();
151 while (cur
!= to_delete
.end()) {
152 ResourceMap::iterator found_resource
= live_resources_
.find(*cur
);
153 if (found_resource
!= live_resources_
.end())
154 found_resource
->second
.first
->NotifyInstanceWasDeleted();
158 instance_map_
.erase(instance
);
161 int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance
) const {
162 CheckThreadingPreconditions();
163 InstanceMap::const_iterator found
= instance_map_
.find(instance
);
164 if (found
== instance_map_
.end())
166 return static_cast<int>(found
->second
->resources
.size());
169 PP_Resource
ResourceTracker::AddResource(Resource
* object
) {
170 CheckThreadingPreconditions();
171 // If the plugin manages to create too many resources, don't do crazy stuff.
172 if (last_resource_value_
== kMaxPPId
)
175 // Allocate an ID. Note there's a rare error condition below that means we
176 // could end up not using |new_id|, but that's harmless.
177 PP_Resource new_id
= MakeTypedId(++last_resource_value_
, PP_ID_TYPE_RESOURCE
);
179 // Some objects have a 0 instance, meaning they aren't associated with any
180 // instance, so they won't be in |instance_map_|. This is (as of this writing)
181 // only true of the PPB_MessageLoop resource for the main thread.
182 if (object
->pp_instance()) {
183 InstanceMap::iterator found
= instance_map_
.find(object
->pp_instance());
184 if (found
== instance_map_
.end()) {
185 // If you hit this, it's likely somebody forgot to call DidCreateInstance,
186 // the resource was created with an invalid PP_Instance, or the renderer
187 // side tried to create a resource for a plugin that crashed/exited. This
188 // could happen for OOP plugins where due to reentrancies in context of
189 // outgoing sync calls the renderer can send events after a plugin has
191 DLOG(INFO
) << "Failed to find plugin instance in instance map";
194 found
->second
->resources
.insert(new_id
);
197 live_resources_
[new_id
] = ResourceAndRefCount(object
, 0);
201 void ResourceTracker::RemoveResource(Resource
* object
) {
202 CheckThreadingPreconditions();
203 PP_Resource pp_resource
= object
->pp_resource();
204 InstanceMap::iterator found
= instance_map_
.find(object
->pp_instance());
205 if (found
!= instance_map_
.end())
206 found
->second
->resources
.erase(pp_resource
);
207 live_resources_
.erase(pp_resource
);
210 void ResourceTracker::LastPluginRefWasDeleted(Resource
* object
) {
211 // Bug http://crbug.com/134611 indicates that sometimes the resource tracker
212 // is null here. This should never be the case since if we have a resource in
213 // the tracker, it should always have a valid instance associated with it
214 // (except for the resource for the main thread's message loop, which has
215 // instance set to 0).
216 // As a result, we do some CHECKs here to see what types of problems the
217 // instance might have before dispatching.
219 // TODO(brettw) remove these checks when this bug is no longer relevant.
220 // Note, we do an imperfect check here; this might be a loop that's not the
222 const bool is_message_loop
= (object
->AsPPB_MessageLoop_API() != NULL
);
223 CHECK(object
->pp_instance() || is_message_loop
);
224 CallbackTracker
* callback_tracker
=
225 PpapiGlobals::Get()->GetCallbackTrackerForInstance(object
->pp_instance());
226 CHECK(callback_tracker
|| is_message_loop
);
227 if (callback_tracker
)
228 callback_tracker
->PostAbortForResource(object
->pp_resource());
229 object
->NotifyLastPluginRefWasDeleted();