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