Disable DeviceOrientationEventPumpTest.* under Valgrind on Mac
[chromium-blink-merge.git] / ppapi / shared_impl / resource_tracker.cc
blob5d7d01ad7ab3703c265cdc9f32e3afacb6cc1d5a
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"
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"
16 namespace ppapi {
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());
30 #ifndef NDEBUG
31 ProxyLock::AssertAcquired();
32 #endif
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())
39 return NULL;
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())
49 return;
51 // Prevent overflow of refcount.
52 if (i->second.second ==
53 std::numeric_limits<ResourceAndRefCount::second_type>::max())
54 return;
56 // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
57 // on its behalf.
58 if (i->second.second == 0)
59 i->second.first->AddRef();
61 i->second.second++;
62 return;
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())
71 return;
73 // Prevent underflow of refcount.
74 if (i->second.second == 0)
75 return;
77 i->second.second--;
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
83 // FROM OUR LIST.
84 i->second.first->Release();
88 void ResourceTracker::ReleaseResourceSoon(PP_Resource res) {
89 base::MessageLoop::current()->PostNonNestableTask(
90 FROM_HERE,
91 RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource,
92 weak_ptr_factory_.GetWeakPtr(),
93 res)));
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
100 // instead.
101 if (instance_map_.find(instance) != instance_map_.end())
102 return;
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
112 // instead.
113 if (found_instance == instance_map_.end())
114 return;
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
128 // to it.
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.
138 resource->Release();
142 cur++;
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
148 // gone.
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();
155 cur++;
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())
165 return 0;
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)
173 return 0;
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
190 // exited.
191 DLOG(INFO) << "Failed to find plugin instance in instance map";
192 return 0;
194 found->second->resources.insert(new_id);
197 live_resources_[new_id] = ResourceAndRefCount(object, 0);
198 return new_id;
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
221 // main one.
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();
232 } // namespace ppapi