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