|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ppapi/shared_impl/resource_tracker.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "ppapi/shared_impl/callback_tracker.h" | 
|  | #include "ppapi/shared_impl/id_assignment.h" | 
|  | #include "ppapi/shared_impl/ppapi_globals.h" | 
|  | #include "ppapi/shared_impl/proxy_lock.h" | 
|  | #include "ppapi/shared_impl/resource.h" | 
|  |  | 
|  | namespace ppapi { | 
|  |  | 
|  | ResourceTracker::ResourceTracker(ThreadMode thread_mode) | 
|  | : last_resource_value_(0), weak_ptr_factory_(this) { | 
|  | if (thread_mode == SINGLE_THREADED) | 
|  | thread_checker_.reset(new base::ThreadChecker); | 
|  | } | 
|  |  | 
|  | ResourceTracker::~ResourceTracker() {} | 
|  |  | 
|  | void ResourceTracker::CheckThreadingPreconditions() const { | 
|  | DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread()); | 
|  | #ifndef NDEBUG | 
|  | ProxyLock::AssertAcquired(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | Resource* ResourceTracker::GetResource(PP_Resource res) const { | 
|  | CheckThreadingPreconditions(); | 
|  | ResourceMap::const_iterator i = live_resources_.find(res); | 
|  | if (i == live_resources_.end()) | 
|  | return NULL; | 
|  | return i->second.first; | 
|  | } | 
|  |  | 
|  | void ResourceTracker::AddRefResource(PP_Resource res) { | 
|  | CheckThreadingPreconditions(); | 
|  | DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE)) | 
|  | << res << " is not a PP_Resource."; | 
|  |  | 
|  | DCHECK(CanOperateOnResource(res)); | 
|  |  | 
|  | ResourceMap::iterator i = live_resources_.find(res); | 
|  | if (i == live_resources_.end()) | 
|  | return; | 
|  |  | 
|  | // Prevent overflow of refcount. | 
|  | if (i->second.second == | 
|  | std::numeric_limits<ResourceAndRefCount::second_type>::max()) | 
|  | return; | 
|  |  | 
|  | // When we go from 0 to 1 plugin ref count, keep an additional "real" ref | 
|  | // on its behalf. | 
|  | if (i->second.second == 0) | 
|  | i->second.first->AddRef(); | 
|  |  | 
|  | i->second.second++; | 
|  | return; | 
|  | } | 
|  |  | 
|  | void ResourceTracker::ReleaseResource(PP_Resource res) { | 
|  | CheckThreadingPreconditions(); | 
|  | DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE)) | 
|  | << res << " is not a PP_Resource."; | 
|  |  | 
|  | DCHECK(CanOperateOnResource(res)); | 
|  |  | 
|  | ResourceMap::iterator i = live_resources_.find(res); | 
|  | if (i == live_resources_.end()) | 
|  | return; | 
|  |  | 
|  | // Prevent underflow of refcount. | 
|  | if (i->second.second == 0) | 
|  | return; | 
|  |  | 
|  | i->second.second--; | 
|  | if (i->second.second == 0) { | 
|  | LastPluginRefWasDeleted(i->second.first); | 
|  |  | 
|  | // When we go from 1 to 0 plugin ref count, free the additional "real" ref | 
|  | // on its behalf. THIS WILL MOST LIKELY RELEASE THE OBJECT AND REMOVE IT | 
|  | // FROM OUR LIST. | 
|  | i->second.first->Release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ResourceTracker::DidCreateInstance(PP_Instance instance) { | 
|  | CheckThreadingPreconditions(); | 
|  | // Due to the infrastructure of some tests, the instance is registered | 
|  | // twice in a few cases. It would be nice not to do that and assert here | 
|  | // instead. | 
|  | if (instance_map_.find(instance) != instance_map_.end()) | 
|  | return; | 
|  | instance_map_[instance] = base::WrapUnique(new InstanceData); | 
|  | } | 
|  |  | 
|  | void ResourceTracker::DidDeleteInstance(PP_Instance instance) { | 
|  | CheckThreadingPreconditions(); | 
|  | InstanceMap::iterator found_instance = instance_map_.find(instance); | 
|  |  | 
|  | // Due to the infrastructure of some tests, the instance is unregistered | 
|  | // twice in a few cases. It would be nice not to do that and assert here | 
|  | // instead. | 
|  | if (found_instance == instance_map_.end()) | 
|  | return; | 
|  |  | 
|  | InstanceData& data = *found_instance->second; | 
|  |  | 
|  | // Force release all plugin references to resources associated with the | 
|  | // deleted instance. Make a copy since as we iterate through them, each one | 
|  | // will remove itself from the tracking info individually. | 
|  | ResourceSet to_delete = data.resources; | 
|  | ResourceSet::iterator cur = to_delete.begin(); | 
|  | while (cur != to_delete.end()) { | 
|  | // Note that it's remotely possible for the object to already be deleted | 
|  | // from the live resources. One case is if a resource object is holding | 
|  | // the last ref to another. When we release the first one, it will release | 
|  | // the second one. So the second one will be gone when we eventually get | 
|  | // to it. | 
|  | ResourceMap::iterator found_resource = live_resources_.find(*cur); | 
|  | if (found_resource != live_resources_.end()) { | 
|  | Resource* resource = found_resource->second.first; | 
|  | if (found_resource->second.second > 0) { | 
|  | LastPluginRefWasDeleted(resource); | 
|  | found_resource->second.second = 0; | 
|  |  | 
|  | // This will most likely delete the resource object and remove it | 
|  | // from the live_resources_ list. | 
|  | resource->Release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | cur++; | 
|  | } | 
|  |  | 
|  | // In general the above pass will delete all the resources and there won't | 
|  | // be any left in the map. However, if parts of the implementation are still | 
|  | // holding on to internal refs, we need to tell them that the instance is | 
|  | // gone. | 
|  | to_delete = data.resources; | 
|  | cur = to_delete.begin(); | 
|  | while (cur != to_delete.end()) { | 
|  | ResourceMap::iterator found_resource = live_resources_.find(*cur); | 
|  | if (found_resource != live_resources_.end()) | 
|  | found_resource->second.first->NotifyInstanceWasDeleted(); | 
|  | cur++; | 
|  | } | 
|  |  | 
|  | instance_map_.erase(instance); | 
|  | } | 
|  |  | 
|  | int ResourceTracker::GetLiveObjectsForInstance(PP_Instance instance) const { | 
|  | CheckThreadingPreconditions(); | 
|  | InstanceMap::const_iterator found = instance_map_.find(instance); | 
|  | if (found == instance_map_.end()) | 
|  | return 0; | 
|  | return static_cast<int>(found->second->resources.size()); | 
|  | } | 
|  |  | 
|  | void ResourceTracker::UseOddResourceValueInDebugMode() { | 
|  | #if !defined(NDEBUG) | 
|  | DCHECK_EQ(0, last_resource_value_); | 
|  |  | 
|  | ++last_resource_value_; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | PP_Resource ResourceTracker::AddResource(Resource* object) { | 
|  | CheckThreadingPreconditions(); | 
|  | // If the plugin manages to create too many resources, don't do crazy stuff. | 
|  | if (last_resource_value_ >= kMaxPPId) | 
|  | return 0; | 
|  |  | 
|  | // Allocate an ID. Note there's a rare error condition below that means we | 
|  | // could end up not using |new_id|, but that's harmless. | 
|  | PP_Resource new_id = MakeTypedId(GetNextResourceValue(), PP_ID_TYPE_RESOURCE); | 
|  |  | 
|  | // Some objects have a 0 instance, meaning they aren't associated with any | 
|  | // instance, so they won't be in |instance_map_|. This is (as of this writing) | 
|  | // only true of the PPB_MessageLoop resource for the main thread. | 
|  | if (object->pp_instance()) { | 
|  | InstanceMap::iterator found = instance_map_.find(object->pp_instance()); | 
|  | if (found == instance_map_.end()) { | 
|  | // If you hit this, it's likely somebody forgot to call DidCreateInstance, | 
|  | // the resource was created with an invalid PP_Instance, or the renderer | 
|  | // side tried to create a resource for a plugin that crashed/exited. This | 
|  | // could happen for OOP plugins where due to reentrancies in context of | 
|  | // outgoing sync calls the renderer can send events after a plugin has | 
|  | // exited. | 
|  | VLOG(1) << "Failed to find plugin instance in instance map"; | 
|  | return 0; | 
|  | } | 
|  | found->second->resources.insert(new_id); | 
|  | } | 
|  |  | 
|  | live_resources_[new_id] = ResourceAndRefCount(object, 0); | 
|  | return new_id; | 
|  | } | 
|  |  | 
|  | void ResourceTracker::RemoveResource(Resource* object) { | 
|  | CheckThreadingPreconditions(); | 
|  | PP_Resource pp_resource = object->pp_resource(); | 
|  | InstanceMap::iterator found = instance_map_.find(object->pp_instance()); | 
|  | if (found != instance_map_.end()) | 
|  | found->second->resources.erase(pp_resource); | 
|  | live_resources_.erase(pp_resource); | 
|  | } | 
|  |  | 
|  | void ResourceTracker::LastPluginRefWasDeleted(Resource* object) { | 
|  | // Bug http://crbug.com/134611 indicates that sometimes the resource tracker | 
|  | // is null here. This should never be the case since if we have a resource in | 
|  | // the tracker, it should always have a valid instance associated with it | 
|  | // (except for the resource for the main thread's message loop, which has | 
|  | // instance set to 0). | 
|  | // As a result, we do some CHECKs here to see what types of problems the | 
|  | // instance might have before dispatching. | 
|  | // | 
|  | // TODO(brettw) remove these checks when this bug is no longer relevant. | 
|  | // Note, we do an imperfect check here; this might be a loop that's not the | 
|  | // main one. | 
|  | const bool is_message_loop = (object->AsPPB_MessageLoop_API() != NULL); | 
|  | CHECK(object->pp_instance() || is_message_loop); | 
|  | CallbackTracker* callback_tracker = | 
|  | PpapiGlobals::Get()->GetCallbackTrackerForInstance(object->pp_instance()); | 
|  | CHECK(callback_tracker || is_message_loop); | 
|  | if (callback_tracker) | 
|  | callback_tracker->PostAbortForResource(object->pp_resource()); | 
|  | object->NotifyLastPluginRefWasDeleted(); | 
|  | } | 
|  |  | 
|  | int32_t ResourceTracker::GetNextResourceValue() { | 
|  | #if defined(NDEBUG) | 
|  | return ++last_resource_value_; | 
|  | #else | 
|  | // In debug mode, the least significant bit indicates which side (renderer | 
|  | // or plugin process) created the resource. Increment by 2 so it's always the | 
|  | // same. | 
|  | last_resource_value_ += 2; | 
|  | return last_resource_value_; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool ResourceTracker::CanOperateOnResource(PP_Resource res) { | 
|  | #if defined(NDEBUG) | 
|  | return true; | 
|  | #else | 
|  | // The invalid PP_Resource value could appear at both sides. | 
|  | if (res == 0) | 
|  | return true; | 
|  |  | 
|  | // Skipping the type bits, the least significant bit of |res| should be the | 
|  | // same as that of |last_resource_value_|. | 
|  | return ((res >> kPPIdTypeBits) & 1) == (last_resource_value_ & 1); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | }  // namespace ppapi |