chromium/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_resource_manager.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "fake_ppapi/fake_resource_manager.h"
#include "gtest/gtest.h"
#include "sdk_util/auto_lock.h"

FakeResourceManager::FakeResourceManager() : next_handle_(1) {}

FakeResourceManager::~FakeResourceManager() {
  // The ref counts for all resources should be zero.
  for (ResourceMap::iterator iter = resource_map_.begin();
       iter != resource_map_.end();
       ++iter) {
    const FakeResourceTracker* resource_tracker = iter->second;
    EXPECT_EQ(0, resource_tracker->ref_count()) << "Leaked resource "
                                                << resource_tracker->classname()
                                                << "(" << iter->first
                                                << "), created at "
                                                << resource_tracker->file()
                                                << ":"
                                                << resource_tracker->line();
    delete resource_tracker;
  }
}

PP_Resource FakeResourceManager::Create(FakeResource* resource,
                                        const char* classname,
                                        const char* file,
                                        int line) {
  AUTO_LOCK(lock_);
  PP_Resource handle = next_handle_++;
  FakeResourceTracker* resource_tracker =
      new FakeResourceTracker(resource, classname, file, line);
  std::pair<ResourceMap::iterator, bool> result =
      resource_map_.insert(ResourceMap::value_type(handle, resource_tracker));
  EXPECT_TRUE(result.second);
  result.first->second->AddRef();
  return handle;
}

void FakeResourceManager::AddRef(PP_Resource handle) {
  AUTO_LOCK(lock_);
  ResourceMap::iterator iter = resource_map_.find(handle);
  ASSERT_NE(resource_map_.end(), iter) << "AddRefing unknown resource "
                                       << handle;

  FakeResourceTracker* resource_tracker = iter->second;
  EXPECT_LT(0, resource_tracker->ref_count()) << "AddRefing freed resource "
                                              << resource_tracker->classname()
                                              << "(" << handle
                                              << "), created at "
                                              << resource_tracker->file() << ":"
                                              << resource_tracker->line();
  resource_tracker->AddRef();
}

void FakeResourceManager::Release(PP_Resource handle) {
  if (handle == 0)
    return;

  sdk_util::AutoLock lock(lock_);
  ResourceMap::iterator iter = resource_map_.find(handle);
  ASSERT_NE(resource_map_.end(), iter) << "Releasing unknown resource "
                                       << handle;

  FakeResourceTracker* resource_tracker = iter->second;
  EXPECT_LT(0, resource_tracker->ref_count()) << "Releasing freed resource "
                                              << resource_tracker->classname()
                                              << "(" << handle
                                              << "), created at "
                                              << resource_tracker->file() << ":"
                                              << resource_tracker->line();
  resource_tracker->Release();
  // It's OK to access the tracker when its refcount is zero; it doesn't
  // actually destroy the object until the manager is destroyed.
  if (resource_tracker->ref_count() == 0) {
    // Remove the resource from this tracker.
    FakeResource* resource = resource_tracker->Pass();
    // Release the lock before we call Destroy; resources can call
    // FakeResourceManager::Release(), which will deadlock if we are already
    // holding the lock.
    lock.Unlock();

    resource->Destroy();
    delete resource;
  }
}

FakeResourceTracker* FakeResourceManager::Get(PP_Resource handle,
                                              bool not_found_ok) {
  AUTO_LOCK(lock_);
  ResourceMap::iterator iter = resource_map_.find(handle);
  if (iter == resource_map_.end()) {
    if (!not_found_ok) {
      // Can't use FAIL() because it tries to return void.
      EXPECT_TRUE(false) << "Trying to get resource " << handle
                         << " that doesn't exist!";
    }

    return NULL;
  }

  FakeResourceTracker* resource_tracker = iter->second;
  EXPECT_LT(0, resource_tracker->ref_count()) << "Accessing freed resource "
                                              << resource_tracker->classname()
                                              << "(" << handle
                                              << "), created at "
                                              << resource_tracker->file() << ":"
                                              << resource_tracker->line();

  return iter->second;
}

FakeResourceTracker::FakeResourceTracker(FakeResource* resource,
                                         const char* classname,
                                         const char* file,
                                         int line)
    : resource_(resource),
      classname_(classname),
      file_(file),
      line_(line),
      ref_count_(0) {}

FakeResourceTracker::~FakeResourceTracker() { delete resource_; }

bool FakeResourceTracker::CheckType(const char* other_classname) const {
  if (strcmp(other_classname, classname_) != 0) {
    // Repeat the expectation, just to print out a nice error message before we
    // crash. :)
    EXPECT_STREQ(classname_, other_classname);
    return false;
  }

  return true;
}