root/ppapi/shared_impl/var_tracker.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. track_with_no_reference_count
  2. track_with_no_reference_count
  3. CheckThreadingPreconditions
  4. AddVar
  5. GetVar
  6. GetVar
  7. AddRefVar
  8. AddRefVar
  9. ReleaseVar
  10. ReleaseVar
  11. AddVarInternal
  12. GetLiveVar
  13. GetRefCountForObject
  14. GetTrackedWithNoReferenceCountForObject
  15. IsVarTypeRefcounted
  16. GetLiveVar
  17. GetLiveVar
  18. MakeArrayBufferPPVar
  19. MakeArrayBufferPPVar
  20. MakeArrayBufferVar
  21. MakeArrayBufferPPVar
  22. MakeResourcePPVar
  23. GetLiveVars
  24. TrackedObjectGettingOneRef
  25. ObjectGettingZeroRef
  26. DeleteObjectInfoIfNecessary

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ppapi/shared_impl/var_tracker.h"

#include <string.h>

#include <limits>

#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "ppapi/shared_impl/host_resource.h"
#include "ppapi/shared_impl/id_assignment.h"
#include "ppapi/shared_impl/proxy_lock.h"
#include "ppapi/shared_impl/resource_var.h"
#include "ppapi/shared_impl/var.h"

namespace ppapi {

VarTracker::VarInfo::VarInfo()
    : var(), ref_count(0), track_with_no_reference_count(0) {}

VarTracker::VarInfo::VarInfo(Var* v, int input_ref_count)
    : var(v), ref_count(input_ref_count), track_with_no_reference_count(0) {}

VarTracker::VarTracker(ThreadMode thread_mode) : last_var_id_(0) {
  if (thread_mode == SINGLE_THREADED)
    thread_checker_.reset(new base::ThreadChecker);
}

VarTracker::~VarTracker() {}

void VarTracker::CheckThreadingPreconditions() const {
  DCHECK(!thread_checker_ || thread_checker_->CalledOnValidThread());
#ifndef NDEBUG
  ProxyLock::AssertAcquired();
#endif
}

int32 VarTracker::AddVar(Var* var) {
  CheckThreadingPreconditions();

  return AddVarInternal(var, ADD_VAR_TAKE_ONE_REFERENCE);
}

Var* VarTracker::GetVar(int32 var_id) const {
  CheckThreadingPreconditions();

  VarMap::const_iterator result = live_vars_.find(var_id);
  if (result == live_vars_.end())
    return NULL;
  return result->second.var.get();
}

Var* VarTracker::GetVar(const PP_Var& var) const {
  CheckThreadingPreconditions();

  if (!IsVarTypeRefcounted(var.type))
    return NULL;
  return GetVar(static_cast<int32>(var.value.as_id));
}

bool VarTracker::AddRefVar(int32 var_id) {
  CheckThreadingPreconditions();

  DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
      << var_id << " is not a PP_Var ID.";
  VarMap::iterator found = live_vars_.find(var_id);
  if (found == live_vars_.end()) {
    NOTREACHED();  // Invalid var.
    return false;
  }

  VarInfo& info = found->second;
  if (info.ref_count == 0) {
    // All live vars with no refcount should be tracked objects.
    DCHECK(info.track_with_no_reference_count > 0);
    DCHECK(info.var->GetType() == PP_VARTYPE_OBJECT);

    TrackedObjectGettingOneRef(found);
  }

  // Basic refcount increment.
  info.ref_count++;
  return true;
}

bool VarTracker::AddRefVar(const PP_Var& var) {
  CheckThreadingPreconditions();

  if (!IsVarTypeRefcounted(var.type))
    return true;
  return AddRefVar(static_cast<int32>(var.value.as_id));
}

bool VarTracker::ReleaseVar(int32 var_id) {
  CheckThreadingPreconditions();

  DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR))
      << var_id << " is not a PP_Var ID.";
  VarMap::iterator found = live_vars_.find(var_id);
  if (found == live_vars_.end())
    return false;

  VarInfo& info = found->second;
  if (info.ref_count == 0) {
    NOTREACHED() << "Releasing an object with zero ref";
    return false;
  }
  info.ref_count--;

  if (info.ref_count == 0) {
    // Hold a reference to the Var until it is erased so that we don't re-enter
    // live_vars_.erase() during deletion.
    // TODO(raymes): Make deletion of Vars iterative instead of recursive.
    scoped_refptr<Var> var(info.var);
    if (var->GetType() == PP_VARTYPE_OBJECT) {
      // Objects have special requirements and may not necessarily be released
      // when the refcount goes to 0.
      ObjectGettingZeroRef(found);
    } else {
      // All other var types can just be released.
      DCHECK(info.track_with_no_reference_count == 0);
      var->ResetVarID();
      live_vars_.erase(found);
    }
  }
  return true;
}

bool VarTracker::ReleaseVar(const PP_Var& var) {
  CheckThreadingPreconditions();

  if (!IsVarTypeRefcounted(var.type))
    return false;
  return ReleaseVar(static_cast<int32>(var.value.as_id));
}

int32 VarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
  // If the plugin manages to create millions of strings.
  if (last_var_id_ == std::numeric_limits<int32>::max() >> kPPIdTypeBits)
    return 0;

  int32 new_id = MakeTypedId(++last_var_id_, PP_ID_TYPE_VAR);
  std::pair<VarMap::iterator, bool> was_inserted =
      live_vars_.insert(std::make_pair(
          new_id, VarInfo(var, mode == ADD_VAR_TAKE_ONE_REFERENCE ? 1 : 0)));
  // We should never insert an ID that already exists.
  DCHECK(was_inserted.second);

  return new_id;
}

VarTracker::VarMap::iterator VarTracker::GetLiveVar(int32 id) {
  return live_vars_.find(id);
}

int VarTracker::GetRefCountForObject(const PP_Var& plugin_object) {
  CheckThreadingPreconditions();

  VarMap::iterator found = GetLiveVar(plugin_object);
  if (found == live_vars_.end())
    return -1;
  return found->second.ref_count;
}

int VarTracker::GetTrackedWithNoReferenceCountForObject(
    const PP_Var& plugin_object) {
  CheckThreadingPreconditions();

  VarMap::iterator found = GetLiveVar(plugin_object);
  if (found == live_vars_.end())
    return -1;
  return found->second.track_with_no_reference_count;
}

// static
bool VarTracker::IsVarTypeRefcounted(PP_VarType type) {
  return type >= PP_VARTYPE_STRING;
}

VarTracker::VarMap::iterator VarTracker::GetLiveVar(const PP_Var& var) {
  return live_vars_.find(static_cast<int32>(var.value.as_id));
}

VarTracker::VarMap::const_iterator VarTracker::GetLiveVar(const PP_Var& var)
    const {
  return live_vars_.find(static_cast<int32>(var.value.as_id));
}

PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes) {
  CheckThreadingPreconditions();

  scoped_refptr<ArrayBufferVar> array_buffer(CreateArrayBuffer(size_in_bytes));
  if (!array_buffer.get())
    return PP_MakeNull();
  return array_buffer->GetPPVar();
}

PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
                                        const void* data) {
  CheckThreadingPreconditions();

  ArrayBufferVar* array_buffer = MakeArrayBufferVar(size_in_bytes, data);
  return array_buffer ? array_buffer->GetPPVar() : PP_MakeNull();
}

ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes,
                                               const void* data) {
  CheckThreadingPreconditions();

  ArrayBufferVar* array_buffer(CreateArrayBuffer(size_in_bytes));
  if (!array_buffer)
    return NULL;
  memcpy(array_buffer->Map(), data, size_in_bytes);
  return array_buffer;
}

PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes,
                                        base::SharedMemoryHandle handle) {
  CheckThreadingPreconditions();

  scoped_refptr<ArrayBufferVar> array_buffer(
      CreateShmArrayBuffer(size_in_bytes, handle));
  if (!array_buffer.get())
    return PP_MakeNull();
  return array_buffer->GetPPVar();
}

PP_Var VarTracker::MakeResourcePPVar(PP_Resource pp_resource) {
  CheckThreadingPreconditions();

  ResourceVar* resource_var = MakeResourceVar(pp_resource);
  return resource_var ? resource_var->GetPPVar() : PP_MakeNull();
}

std::vector<PP_Var> VarTracker::GetLiveVars() {
  CheckThreadingPreconditions();

  std::vector<PP_Var> var_vector;
  var_vector.reserve(live_vars_.size());
  for (VarMap::const_iterator iter = live_vars_.begin();
       iter != live_vars_.end();
       ++iter) {
    var_vector.push_back(iter->second.var->GetPPVar());
  }
  return var_vector;
}

void VarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator obj) {
  // Anybody using tracked objects should override this.
  NOTREACHED();
}

void VarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
  DeleteObjectInfoIfNecessary(iter);
}

bool VarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
  if (iter->second.ref_count != 0 ||
      iter->second.track_with_no_reference_count != 0)
    return false;  // Object still alive.
  iter->second.var->ResetVarID();
  live_vars_.erase(iter);
  return true;
}

}  // namespace ppapi

/* [<][>][^][v][top][bottom][index][help] */