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   ResourceMap::iterator i = live_resources_.find(res);
     48   if (i == live_resources_.end())
     49     return;
     50 
     51   // Prevent overflow of refcount.
     52   if (i->second.second ==
     53       std::numeric_limits<ResourceAndRefCount::second_type>::max())
     54     return;
     55 
     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();
     60 
     61   i->second.second++;
     62   return;
     63 }
     64 
     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;
     72 
     73   // Prevent underflow of refcount.
     74   if (i->second.second == 0)
     75     return;
     76 
     77   i->second.second--;
     78   if (i->second.second == 0) {
     79     LastPluginRefWasDeleted(i->second.first);
     80 
     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();
     85   }
     86 }
     87 
     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)));
     94 }
     95 
     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);
    104 }
    105 
    106 void ResourceTracker::DidDeleteInstance(PP_Instance instance) {
    107   CheckThreadingPreconditions();
    108   InstanceMap::iterator found_instance = instance_map_.find(instance);
    109 
    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;
    115 
    116   InstanceData& data = *found_instance->second;
    117 
    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;
    135 
    136         // This will most likely delete the resource object and remove it
    137         // from the live_resources_ list.
    138         resource->Release();
    139       }
    140     }
    141 
    142     cur++;
    143   }
    144 
    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++;
    156   }
    157 
    158   instance_map_.erase(instance);
    159 }
    160 
    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());
    167 }
    168 
    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;
    174 
    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);
    178 
    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;
    193     }
    194     found->second->resources.insert(new_id);
    195   }
    196 
    197   live_resources_[new_id] = ResourceAndRefCount(object, 0);
    198   return new_id;
    199 }
    200 
    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);
    208 }
    209 
    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.
    218   //
    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();
    230 }
    231 
    232 }  // namespace ppapi
    233