Stop retring GCM unregistration requests after reaching maximum number
[chromium-blink-merge.git] / ppapi / shared_impl / resource_tracker.cc
blob208212c95dc7221ca05b16946812931597f94b32
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), 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());
28 #ifndef NDEBUG
29 ProxyLock::AssertAcquired();
30 #endif
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())
37 return NULL;
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())
50 return;
52 // Prevent overflow of refcount.
53 if (i->second.second ==
54 std::numeric_limits<ResourceAndRefCount::second_type>::max())
55 return;
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();
62 i->second.second++;
63 return;
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())
75 return;
77 // Prevent underflow of refcount.
78 if (i->second.second == 0)
79 return;
81 i->second.second--;
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
87 // FROM OUR LIST.
88 i->second.first->Release();
92 void ResourceTracker::DidCreateInstance(PP_Instance instance) {
93 CheckThreadingPreconditions();
94 // Due to the infrastructure of some tests, the instance is registered
95 // twice in a few cases. It would be nice not to do that and assert here
96 // instead.
97 if (instance_map_.find(instance) != instance_map_.end())
98 return;
99 instance_map_[instance] = linked_ptr<InstanceData>(new InstanceData);
102 void ResourceTracker::DidDeleteInstance(PP_Instance instance) {
103 CheckThreadingPreconditions();
104 InstanceMap::iterator found_instance = instance_map_.find(instance);
106 // Due to the infrastructure of some tests, the instance is unregistered
107 // twice in a few cases. It would be nice not to do that and assert here
108 // instead.
109 if (found_instance == instance_map_.end())
110 return;
112 InstanceData& data = *found_instance->second;
114 // Force release all plugin references to resources associated with the
115 // deleted instance. Make a copy since as we iterate through them, each one
116 // will remove itself from the tracking info individually.
117 ResourceSet to_delete = data.resources;
118 ResourceSet::iterator cur = to_delete.begin();
119 while (cur != to_delete.end()) {
120 // Note that it's remotely possible for the object to already be deleted
121 // from the live resources. One case is if a resource object is holding
122 // the last ref to another. When we release the first one, it will release
123 // the second one. So the second one will be gone when we eventually get
124 // to it.
125 ResourceMap::iterator found_resource = live_resources_.find(*cur);
126 if (found_resource != live_resources_.end()) {
127 Resource* resource = found_resource->second.first;
128 if (found_resource->second.second > 0) {
129 LastPluginRefWasDeleted(resource);
130 found_resource->second.second = 0;
132 // This will most likely delete the resource object and remove it
133 // from the live_resources_ list.
134 resource->Release();
138 cur++;
141 // In general the above pass will delete all the resources and there won't
142 // be any left in the map. However, if parts of the implementation are still
143 // holding on to internal refs, we need to tell them that the instance is
144 // gone.
145 to_delete = data.resources;
146 cur = to_delete.begin();
147 while (cur != to_delete.end()) {
148 ResourceMap::iterator found_resource = live_resources_.find(*cur);
149 if (found_resource != live_resources_.end())
150 found_resource->second.first->NotifyInstanceWasDeleted();
151 cur++;
154 instance_map_.erase(instance);
157 int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance) const {
158 CheckThreadingPreconditions();
159 InstanceMap::const_iterator found = instance_map_.find(instance);
160 if (found == instance_map_.end())
161 return 0;
162 return static_cast<int>(found->second->resources.size());
165 void ResourceTracker::UseOddResourceValueInDebugMode() {
166 #if !defined(NDEBUG)
167 DCHECK_EQ(0, last_resource_value_);
169 ++last_resource_value_;
170 #endif
173 PP_Resource ResourceTracker::AddResource(Resource* object) {
174 CheckThreadingPreconditions();
175 // If the plugin manages to create too many resources, don't do crazy stuff.
176 if (last_resource_value_ >= kMaxPPId)
177 return 0;
179 // Allocate an ID. Note there's a rare error condition below that means we
180 // could end up not using |new_id|, but that's harmless.
181 PP_Resource new_id = MakeTypedId(GetNextResourceValue(), PP_ID_TYPE_RESOURCE);
183 // Some objects have a 0 instance, meaning they aren't associated with any
184 // instance, so they won't be in |instance_map_|. This is (as of this writing)
185 // only true of the PPB_MessageLoop resource for the main thread.
186 if (object->pp_instance()) {
187 InstanceMap::iterator found = instance_map_.find(object->pp_instance());
188 if (found == instance_map_.end()) {
189 // If you hit this, it's likely somebody forgot to call DidCreateInstance,
190 // the resource was created with an invalid PP_Instance, or the renderer
191 // side tried to create a resource for a plugin that crashed/exited. This
192 // could happen for OOP plugins where due to reentrancies in context of
193 // outgoing sync calls the renderer can send events after a plugin has
194 // exited.
195 VLOG(1) << "Failed to find plugin instance in instance map";
196 return 0;
198 found->second->resources.insert(new_id);
201 live_resources_[new_id] = ResourceAndRefCount(object, 0);
202 return new_id;
205 void ResourceTracker::RemoveResource(Resource* object) {
206 CheckThreadingPreconditions();
207 PP_Resource pp_resource = object->pp_resource();
208 InstanceMap::iterator found = instance_map_.find(object->pp_instance());
209 if (found != instance_map_.end())
210 found->second->resources.erase(pp_resource);
211 live_resources_.erase(pp_resource);
214 void ResourceTracker::LastPluginRefWasDeleted(Resource* object) {
215 // Bug http://crbug.com/134611 indicates that sometimes the resource tracker
216 // is null here. This should never be the case since if we have a resource in
217 // the tracker, it should always have a valid instance associated with it
218 // (except for the resource for the main thread's message loop, which has
219 // instance set to 0).
220 // As a result, we do some CHECKs here to see what types of problems the
221 // instance might have before dispatching.
223 // TODO(brettw) remove these checks when this bug is no longer relevant.
224 // Note, we do an imperfect check here; this might be a loop that's not the
225 // main one.
226 const bool is_message_loop = (object->AsPPB_MessageLoop_API() != NULL);
227 CHECK(object->pp_instance() || is_message_loop);
228 CallbackTracker* callback_tracker =
229 PpapiGlobals::Get()->GetCallbackTrackerForInstance(object->pp_instance());
230 CHECK(callback_tracker || is_message_loop);
231 if (callback_tracker)
232 callback_tracker->PostAbortForResource(object->pp_resource());
233 object->NotifyLastPluginRefWasDeleted();
236 int32 ResourceTracker::GetNextResourceValue() {
237 #if defined(NDEBUG)
238 return ++last_resource_value_;
239 #else
240 // In debug mode, the least significant bit indicates which side (renderer
241 // or plugin process) created the resource. Increment by 2 so it's always the
242 // same.
243 last_resource_value_ += 2;
244 return last_resource_value_;
245 #endif
248 bool ResourceTracker::CanOperateOnResource(PP_Resource res) {
249 #if defined(NDEBUG)
250 return true;
251 #else
252 // The invalid PP_Resource value could appear at both sides.
253 if (res == 0)
254 return true;
256 // Skipping the type bits, the least significant bit of |res| should be the
257 // same as that of |last_resource_value_|.
258 return ((res >> kPPIdTypeBits) & 1) == (last_resource_value_ & 1);
259 #endif
262 } // namespace ppapi