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