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