| // 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 |