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