- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / shared_impl / resource_tracker.cc
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.
4
5 #include "ppapi/shared_impl/resource_tracker.h"
6
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"
15
16 namespace ppapi {
17
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);
23 }
24
25 ResourceTracker::~ResourceTracker() {
26 }
27
28 void ResourceTracker::CheckThreadingPreconditions() const {
29   DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
30 #ifndef NDEBUG
31   ProxyLock::AssertAcquired();
32 #endif
33 }
34
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;
41 }
42
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
48   DCHECK(CanOperateOnResource(res));
49
50   ResourceMap::iterator i = live_resources_.find(res);
51   if (i == live_resources_.end())
52     return;
53
54   // Prevent overflow of refcount.
55   if (i->second.second ==
56       std::numeric_limits<ResourceAndRefCount::second_type>::max())
57     return;
58
59   // When we go from 0 to 1 plugin ref count, keep an additional "real" ref
60   // on its behalf.
61   if (i->second.second == 0)
62     i->second.first->AddRef();
63
64   i->second.second++;
65   return;
66 }
67
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.";
72
73   DCHECK(CanOperateOnResource(res));
74
75   ResourceMap::iterator i = live_resources_.find(res);
76   if (i == live_resources_.end())
77     return;
78
79   // Prevent underflow of refcount.
80   if (i->second.second == 0)
81     return;
82
83   i->second.second--;
84   if (i->second.second == 0) {
85     LastPluginRefWasDeleted(i->second.first);
86
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
89     // FROM OUR LIST.
90     i->second.first->Release();
91   }
92 }
93
94 void ResourceTracker::ReleaseResourceSoon(PP_Resource res) {
95   base::MessageLoop::current()->PostNonNestableTask(
96       FROM_HERE,
97       RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource,
98                                 weak_ptr_factory_.GetWeakPtr(),
99                                 res)));
100 }
101
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
106   // instead.
107   if (instance_map_.find(instance) != instance_map_.end())
108     return;
109   instance_map_[instance] = linked_ptr<InstanceData>(new InstanceData);
110 }
111
112 void ResourceTracker::DidDeleteInstance(PP_Instance instance) {
113   CheckThreadingPreconditions();
114   InstanceMap::iterator found_instance = instance_map_.find(instance);
115
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
118   // instead.
119   if (found_instance == instance_map_.end())
120     return;
121
122   InstanceData& data = *found_instance->second;
123
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
134     // to it.
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;
141
142         // This will most likely delete the resource object and remove it
143         // from the live_resources_ list.
144         resource->Release();
145       }
146     }
147
148     cur++;
149   }
150
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
154   // gone.
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();
161     cur++;
162   }
163
164   instance_map_.erase(instance);
165 }
166
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())
171     return 0;
172   return static_cast<int>(found->second->resources.size());
173 }
174
175 void ResourceTracker::UseOddResourceValueInDebugMode() {
176 #if !defined(NDEBUG)
177   DCHECK_EQ(0, last_resource_value_);
178
179   ++last_resource_value_;
180 #endif
181 }
182
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)
187     return 0;
188
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);
192
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
204       // exited.
205       DLOG(INFO) << "Failed to find plugin instance in instance map";
206       return 0;
207     }
208     found->second->resources.insert(new_id);
209   }
210
211   live_resources_[new_id] = ResourceAndRefCount(object, 0);
212   return new_id;
213 }
214
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);
222 }
223
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.
232   //
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
235   // main one.
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();
244 }
245
246 int32 ResourceTracker::GetNextResourceValue() {
247 #if defined(NDEBUG)
248   return ++last_resource_value_;
249 #else
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
252   // same.
253   last_resource_value_ += 2;
254   return last_resource_value_;
255 #endif
256 }
257
258 bool ResourceTracker::CanOperateOnResource(PP_Resource res) {
259 #if defined(NDEBUG)
260   return true;
261 #else
262   // The invalid PP_Resource value could appear at both sides.
263   if (res == 0)
264     return true;
265
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);
269 #endif
270
271 }
272
273 }  // namespace ppapi