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