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