Home | History | Annotate | Download | only in shared_impl
      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
    274