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
19namespace ppapi {
20
21VarTracker::VarInfo::VarInfo()
22    : var(), ref_count(0), track_with_no_reference_count(0) {}
23
24VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
25    : var(v), ref_count(input_ref_count), track_with_no_reference_count(0) {}
26
27VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
28  if (thread_mode == SINGLE_THREADED)
29    thread_checker_.reset(new base::ThreadChecker);
30}
31
32VarTracker::~VarTracker() {}
33
34void VarTracker::CheckThreadingPreconditions() const {
35  DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
36#ifndef NDEBUG
37  ProxyLock::AssertAcquired();
38#endif
39}
40
41int32 VarTracker::AddVar(Var* var) {
42  CheckThreadingPreconditions();
43
44  return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
45}
46
47Var* 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
56Var* 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
64bool 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
89bool 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
97bool 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
132bool 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
140int32 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
155VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
156  return live_vars_.find(id);
157}
158
159int 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
168int 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
179bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
180  return type >= PP_VARTYPE_STRING;
181}
182
183VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
184  return live_vars_.find(static_cast<int32>(var.value.as_id));
185}
186
187VarTracker::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
192PP_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
201PP_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
209ArrayBufferVar* 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
220PP_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
231PP_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
238std::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
251void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
252  // Anybody using tracked objects should override this.
253  NOTREACHED();
254}
255
256void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
257  DeleteObjectInfoIfNecessary(iter);
258}
259
260bool 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