// 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 VarTracker::AddVar(Var* var) { CheckThreadingPreconditions(); return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE); } Var* VarTracker::GetVar(int32 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>(var.value.as_id)); } bool VarTracker::AddRefVar(int32 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>(var.value.as_id)); } bool VarTracker::ReleaseVar(int32 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>(var.value.as_id)); } int32 VarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { // If the plugin manages to create millions of strings. if (last_var_id_ == std::numeric_limits<int32>::max() >> kPPIdTypeBits) return 0; int32 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 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>(var.value.as_id)); } VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(const PP_Var& var) const { return live_vars_.find(static_cast<int32>(var.value.as_id)); } PP_Var VarTracker::MakeArrayBufferPPVar(uint32 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 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 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 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