Home | History | Annotate | Download | only in shared_impl
      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/var_tracker.h"
      6 
      7 #include <string.h>
      8 
      9 #include <limits>
     10 
     11 #include "base/logging.h"
     12 #include "base/memory/shared_memory.h"
     13 #include "ppapi/shared_impl/host_resource.h"
     14 #include "ppapi/shared_impl/id_assignment.h"
     15 #include "ppapi/shared_impl/proxy_lock.h"
     16 #include "ppapi/shared_impl/resource_var.h"
     17 #include "ppapi/shared_impl/var.h"
     18 
     19 namespace ppapi {
     20 
     21 VarTracker::VarInfo::VarInfo()
     22     : var(),
     23       ref_count(0),
     24       track_with_no_reference_count(0) {
     25 }
     26 
     27 VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
     28     : var(v),
     29       ref_count(input_ref_count),
     30       track_with_no_reference_count(0) {
     31 }
     32 
     33 VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
     34   if (thread_mode == SINGLE_THREADED)
     35     thread_checker_.reset(new base::ThreadChecker);
     36 }
     37 
     38 VarTracker::~VarTracker() {
     39 }
     40 
     41 void VarTracker::CheckThreadingPreconditions() const {
     42   DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
     43 #ifndef NDEBUG
     44   ProxyLock::AssertAcquired();
     45 #endif
     46 }
     47 
     48 int32 VarTracker::AddVar(Var* var) {
     49   CheckThreadingPreconditions();
     50 
     51   return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
     52 }
     53 
     54 Var* VarTracker::GetVar(int32 var_id) const {
     55   CheckThreadingPreconditions();
     56 
     57   VarMap::const_iterator result = live_vars_.find(var_id);
     58   if (result == live_vars_.end())
     59     return NULL;
     60   return result->second.var.get();
     61 }
     62 
     63 Var* VarTracker::GetVar(const PP_Var& var) const {
     64   CheckThreadingPreconditions();
     65 
     66   if (!IsVarTypeRefcounted(var.type))
     67     return NULL;
     68   return GetVar(static_cast<int32>(var.value.as_id));
     69 }
     70 
     71 bool VarTracker::AddRefVar(int32 var_id) {
     72   CheckThreadingPreconditions();
     73 
     74   DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
     75       << var_id << " is not a PP_Var ID.";
     76   VarMap::iterator found = live_vars_.find(var_id);
     77   if (found == live_vars_.end()) {
     78     NOTREACHED();  // Invalid var.
     79     return false;
     80   }
     81 
     82   VarInfo& info = found->second;
     83   if (info.ref_count == 0) {
     84     // All live vars with no refcount should be tracked objects.
     85     DCHECK(info.track_with_no_reference_count > 0);
     86     DCHECK(info.var->GetType() == PP_VARTYPE_OBJECT);
     87 
     88     TrackedObjectGettingOneRef(found);
     89   }
     90 
     91   // Basic refcount increment.
     92   info.ref_count++;
     93   return true;
     94 }
     95 
     96 bool VarTracker::AddRefVar(const PP_Var& var) {
     97   CheckThreadingPreconditions();
     98 
     99   if (!IsVarTypeRefcounted(var.type))
    100     return true;
    101   return AddRefVar(static_cast<int32>(var.value.as_id));
    102 }
    103 
    104 bool VarTracker::ReleaseVar(int32 var_id) {
    105   CheckThreadingPreconditions();
    106 
    107   DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
    108       << var_id << " is not a PP_Var ID.";
    109   VarMap::iterator found = live_vars_.find(var_id);
    110   if (found == live_vars_.end())
    111     return false;
    112 
    113   VarInfo& info = found->second;
    114   if (info.ref_count == 0) {
    115     NOTREACHED() << "Releasing an object with zero ref";
    116     return false;
    117   }
    118   info.ref_count--;
    119 
    120   if (info.ref_count == 0) {
    121     // Hold a reference to the Var until it is erased so that we don't re-enter
    122     // live_vars_.erase() during deletion.
    123     // TODO(raymes): Make deletion of Vars iterative instead of recursive.
    124     scoped_refptr<Var> var(info.var);
    125     if (var->GetType() == PP_VARTYPE_OBJECT) {
    126       // Objects have special requirements and may not necessarily be released
    127       // when the refcount goes to 0.
    128       ObjectGettingZeroRef(found);
    129     } else {
    130       // All other var types can just be released.
    131       DCHECK(info.track_with_no_reference_count == 0);
    132       var->ResetVarID();
    133       live_vars_.erase(found);
    134     }
    135   }
    136   return true;
    137 }
    138 
    139 bool VarTracker::ReleaseVar(const PP_Var& var) {
    140   CheckThreadingPreconditions();
    141 
    142   if (!IsVarTypeRefcounted(var.type))
    143     return false;
    144   return ReleaseVar(static_cast<int32>(var.value.as_id));
    145 }
    146 
    147 int32 VarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
    148   // If the plugin manages to create millions of strings.
    149   if (last_var_id_ == std::numeric_limits<int32>::max() >> kPPIdTypeBits)
    150     return 0;
    151 
    152   int32 new_id = MakeTypedId(++last_var_id_, PP_ID_TYPE_VAR);
    153   std::pair<VarMap::iterator, bool> was_inserted =
    154       live_vars_.insert(std::make_pair(new_id,
    155           VarInfo(var, mode == ADD_VAR_TAKE_ONE_REFERENCE ? 1 : 0)));
    156   // We should never insert an ID that already exists.
    157   DCHECK(was_inserted.second);
    158 
    159   return new_id;
    160 }
    161 
    162 VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
    163   return live_vars_.find(id);
    164 }
    165 
    166 int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) {
    167   CheckThreadingPreconditions();
    168 
    169   VarMap::iterator found = GetLiveVar(plugin_object);
    170   if (found == live_vars_.end())
    171     return -1;
    172   return found->second.ref_count;
    173 }
    174 
    175 int VarTracker::GetTrackedWithNoReferenceCountForObject(
    176     const PP_Var& plugin_object) {
    177   CheckThreadingPreconditions();
    178 
    179   VarMap::iterator found = GetLiveVar(plugin_object);
    180   if (found == live_vars_.end())
    181     return -1;
    182   return found->second.track_with_no_reference_count;
    183 }
    184 
    185 // static
    186 bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
    187   return type >= PP_VARTYPE_STRING;
    188 }
    189 
    190 VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
    191   return live_vars_.find(static_cast<int32>(var.value.as_id));
    192 }
    193 
    194 VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(
    195     const PP_Var& var) const {
    196   return live_vars_.find(static_cast<int32>(var.value.as_id));
    197 }
    198 
    199 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes) {
    200   CheckThreadingPreconditions();
    201 
    202   scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes));
    203   if (!array_buffer.get())
    204     return PP_MakeNull();
    205   return array_buffer->GetPPVar();
    206 }
    207 
    208 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
    209                                         const void* data) {
    210   CheckThreadingPreconditions();
    211 
    212   ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data);
    213   return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull();
    214 }
    215 
    216 ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes,
    217                                                const void* data) {
    218   CheckThreadingPreconditions();
    219 
    220   ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes));
    221   if (!array_buffer)
    222     return NULL;
    223   memcpy(array_buffer->Map(), data, size_in_bytes);
    224   return array_buffer;
    225 }
    226 
    227 PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
    228                                         base::SharedMemoryHandle handle) {
    229   CheckThreadingPreconditions();
    230 
    231   scoped_refptr<ArrayBufferVar> array_buffer(
    232       CreateShmArrayBuffer(size_in_bytes, handle));
    233   if (!array_buffer.get())
    234     return PP_MakeNull();
    235   return array_buffer->GetPPVar();
    236 }
    237 
    238 PP_Var VarTracker::MakeResourcePPVar(PP_Resource pp_resource) {
    239   CheckThreadingPreconditions();
    240 
    241   ResourceVar* resource_var = MakeResourceVar(pp_resource);
    242   return resource_var ? resource_var->GetPPVar() : PP_MakeNull();
    243 }
    244 
    245 std::vector<PP_Var> VarTracker::GetLiveVars() {
    246   CheckThreadingPreconditions();
    247 
    248   std::vector<PP_Var> var_vector;
    249   var_vector.reserve(live_vars_.size());
    250   for (VarMap::const_iterator iter = live_vars_.begin();
    251        iter != live_vars_.end();
    252        ++iter) {
    253     var_vector.push_back(iter->second.var->GetPPVar());
    254   }
    255   return var_vector;
    256 }
    257 
    258 void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
    259   // Anybody using tracked objects should override this.
    260   NOTREACHED();
    261 }
    262 
    263 void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
    264   DeleteObjectInfoIfNecessary(iter);
    265 }
    266 
    267 bool VarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
    268   if (iter->second.ref_count != 0 ||
    269       iter->second.track_with_no_reference_count != 0)
    270     return false;  // Object still alive.
    271   iter->second.var->ResetVarID();
    272   live_vars_.erase(iter);
    273   return true;
    274 }
    275 
    276 }  // namespace ppapi
    277