|  | // 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/var_tracker.h" | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #include <limits> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/shared_memory.h" | 
|  | #include "ppapi/shared_impl/host_resource.h" | 
|  | #include "ppapi/shared_impl/id_assignment.h" | 
|  | #include "ppapi/shared_impl/proxy_lock.h" | 
|  | #include "ppapi/shared_impl/resource_var.h" | 
|  | #include "ppapi/shared_impl/var.h" | 
|  |  | 
|  | namespace ppapi { | 
|  |  | 
|  | VarTracker::VarInfo::VarInfo() | 
|  | : var(), ref_count(0), track_with_no_reference_count(0) {} | 
|  |  | 
|  | VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count) | 
|  | : var(v), ref_count(input_ref_count), track_with_no_reference_count(0) {} | 
|  |  | 
|  | VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) { | 
|  | if (thread_mode == SINGLE_THREADED) | 
|  | thread_checker_.reset(new base::ThreadChecker); | 
|  | } | 
|  |  | 
|  | VarTracker::~VarTracker() {} | 
|  |  | 
|  | void VarTracker::CheckThreadingPreconditions() const { | 
|  | DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread()); | 
|  | #ifndef NDEBUG | 
|  | ProxyLock::AssertAcquired(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | int32_t VarTracker::AddVar(Var* var) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE); | 
|  | } | 
|  |  | 
|  | Var* VarTracker::GetVar(int32_t var_id) const { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | VarMap::const_iterator result = live_vars_.find(var_id); | 
|  | if (result == live_vars_.end()) | 
|  | return NULL; | 
|  | return result->second.var.get(); | 
|  | } | 
|  |  | 
|  | Var* VarTracker::GetVar(const PP_Var& var) const { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | if (!IsVarTypeRefcounted(var.type)) | 
|  | return NULL; | 
|  | return GetVar(static_cast<int32_t>(var.value.as_id)); | 
|  | } | 
|  |  | 
|  | bool VarTracker::AddRefVar(int32_t var_id) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR)) | 
|  | << var_id << " is not a PP_Var ID."; | 
|  | VarMap::iterator found = live_vars_.find(var_id); | 
|  | if (found == live_vars_.end()) { | 
|  | NOTREACHED();  // Invalid var. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | VarInfo& info = found->second; | 
|  | if (info.ref_count == 0) { | 
|  | // All live vars with no refcount should be tracked objects. | 
|  | DCHECK(info.track_with_no_reference_count > 0); | 
|  | DCHECK(info.var->GetType() == PP_VARTYPE_OBJECT); | 
|  |  | 
|  | TrackedObjectGettingOneRef(found); | 
|  | } | 
|  |  | 
|  | // Basic refcount increment. | 
|  | info.ref_count++; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VarTracker::AddRefVar(const PP_Var& var) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | if (!IsVarTypeRefcounted(var.type)) | 
|  | return true; | 
|  | return AddRefVar(static_cast<int32_t>(var.value.as_id)); | 
|  | } | 
|  |  | 
|  | bool VarTracker::ReleaseVar(int32_t var_id) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR)) | 
|  | << var_id << " is not a PP_Var ID."; | 
|  | VarMap::iterator found = live_vars_.find(var_id); | 
|  | if (found == live_vars_.end()) | 
|  | return false; | 
|  |  | 
|  | VarInfo& info = found->second; | 
|  | if (info.ref_count == 0) { | 
|  | NOTREACHED() << "Releasing an object with zero ref"; | 
|  | return false; | 
|  | } | 
|  | info.ref_count--; | 
|  |  | 
|  | if (info.ref_count == 0) { | 
|  | // Hold a reference to the Var until it is erased so that we don't re-enter | 
|  | // live_vars_.erase() during deletion. | 
|  | // TODO(raymes): Make deletion of Vars iterative instead of recursive. | 
|  | scoped_refptr<Var> var(info.var); | 
|  | if (var->GetType() == PP_VARTYPE_OBJECT) { | 
|  | // Objects have special requirements and may not necessarily be released | 
|  | // when the refcount goes to 0. | 
|  | ObjectGettingZeroRef(found); | 
|  | } else { | 
|  | // All other var types can just be released. | 
|  | DCHECK(info.track_with_no_reference_count == 0); | 
|  | var->ResetVarID(); | 
|  | live_vars_.erase(found); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VarTracker::ReleaseVar(const PP_Var& var) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | if (!IsVarTypeRefcounted(var.type)) | 
|  | return false; | 
|  | return ReleaseVar(static_cast<int32_t>(var.value.as_id)); | 
|  | } | 
|  |  | 
|  | int32_t VarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { | 
|  | // If the plugin manages to create millions of strings. | 
|  | if (last_var_id_ == std::numeric_limits<int32_t>::max() >> kPPIdTypeBits) | 
|  | return 0; | 
|  |  | 
|  | int32_t new_id = MakeTypedId(++last_var_id_, PP_ID_TYPE_VAR); | 
|  | std::pair<VarMap::iterator, bool> was_inserted = | 
|  | live_vars_.insert(std::make_pair( | 
|  | new_id, VarInfo(var, mode == ADD_VAR_TAKE_ONE_REFERENCE ? 1 : 0))); | 
|  | // We should never insert an ID that already exists. | 
|  | DCHECK(was_inserted.second); | 
|  |  | 
|  | return new_id; | 
|  | } | 
|  |  | 
|  | VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32_t id) { | 
|  | return live_vars_.find(id); | 
|  | } | 
|  |  | 
|  | int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | VarMap::iterator found = GetLiveVar(plugin_object); | 
|  | if (found == live_vars_.end()) | 
|  | return -1; | 
|  | return found->second.ref_count; | 
|  | } | 
|  |  | 
|  | int VarTracker::GetTrackedWithNoReferenceCountForObject( | 
|  | const PP_Var& plugin_object) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | VarMap::iterator found = GetLiveVar(plugin_object); | 
|  | if (found == live_vars_.end()) | 
|  | return -1; | 
|  | return found->second.track_with_no_reference_count; | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool VarTracker::IsVarTypeRefcounted(PP_VarType type) { | 
|  | return type >= PP_VARTYPE_STRING; | 
|  | } | 
|  |  | 
|  | VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) { | 
|  | return live_vars_.find(static_cast<int32_t>(var.value.as_id)); | 
|  | } | 
|  |  | 
|  | VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(const PP_Var& var) | 
|  | const { | 
|  | return live_vars_.find(static_cast<int32_t>(var.value.as_id)); | 
|  | } | 
|  |  | 
|  | PP_Var VarTracker::MakeArrayBufferPPVar(uint32_t size_in_bytes) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes)); | 
|  | if (!array_buffer.get()) | 
|  | return PP_MakeNull(); | 
|  | return array_buffer->GetPPVar(); | 
|  | } | 
|  |  | 
|  | PP_Var VarTracker::MakeArrayBufferPPVar(uint32_t size_in_bytes, | 
|  | const void* data) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data); | 
|  | return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull(); | 
|  | } | 
|  |  | 
|  | ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32_t size_in_bytes, | 
|  | const void* data) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes)); | 
|  | if (!array_buffer) | 
|  | return NULL; | 
|  | memcpy(array_buffer->Map(), data, size_in_bytes); | 
|  | return array_buffer; | 
|  | } | 
|  |  | 
|  | PP_Var VarTracker::MakeArrayBufferPPVar(uint32_t size_in_bytes, | 
|  | base::SharedMemoryHandle handle) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | scoped_refptr<ArrayBufferVar> array_buffer( | 
|  | CreateShmArrayBuffer(size_in_bytes, handle)); | 
|  | if (!array_buffer.get()) | 
|  | return PP_MakeNull(); | 
|  | return array_buffer->GetPPVar(); | 
|  | } | 
|  |  | 
|  | PP_Var VarTracker::MakeResourcePPVar(PP_Resource pp_resource) { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | ResourceVar* resource_var = MakeResourceVar(pp_resource); | 
|  | return resource_var ? resource_var->GetPPVar() : PP_MakeNull(); | 
|  | } | 
|  |  | 
|  | std::vector<PP_Var> VarTracker::GetLiveVars() { | 
|  | CheckThreadingPreconditions(); | 
|  |  | 
|  | std::vector<PP_Var> var_vector; | 
|  | var_vector.reserve(live_vars_.size()); | 
|  | for (VarMap::const_iterator iter = live_vars_.begin(); | 
|  | iter != live_vars_.end(); | 
|  | ++iter) { | 
|  | var_vector.push_back(iter->second.var->GetPPVar()); | 
|  | } | 
|  | return var_vector; | 
|  | } | 
|  |  | 
|  | void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) { | 
|  | // Anybody using tracked objects should override this. | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) { | 
|  | DeleteObjectInfoIfNecessary(iter); | 
|  | } | 
|  |  | 
|  | bool VarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) { | 
|  | if (iter->second.ref_count != 0 || | 
|  | iter->second.track_with_no_reference_count != 0) | 
|  | return false;  // Object still alive. | 
|  | iter->second.var->ResetVarID(); | 
|  | live_vars_.erase(iter); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace ppapi |