1// Copyright 2014 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 "fake_ppapi/fake_resource_manager.h"
6#include "gtest/gtest.h"
7#include "sdk_util/auto_lock.h"
8
9FakeResourceManager::FakeResourceManager() : next_handle_(1) {}
10
11FakeResourceManager::~FakeResourceManager() {
12  // The ref counts for all resources should be zero.
13  for (ResourceMap::iterator iter = resource_map_.begin();
14       iter != resource_map_.end();
15       ++iter) {
16    const FakeResourceTracker* resource_tracker = iter->second;
17    EXPECT_EQ(0, resource_tracker->ref_count()) << "Leaked resource "
18                                                << resource_tracker->classname()
19                                                << "(" << iter->first
20                                                << "), created at "
21                                                << resource_tracker->file()
22                                                << ":"
23                                                << resource_tracker->line();
24    delete resource_tracker;
25  }
26}
27
28PP_Resource FakeResourceManager::Create(FakeResource* resource,
29                                        const char* classname,
30                                        const char* file,
31                                        int line) {
32  AUTO_LOCK(lock_);
33  PP_Resource handle = next_handle_++;
34  FakeResourceTracker* resource_tracker =
35      new FakeResourceTracker(resource, classname, file, line);
36  std::pair<ResourceMap::iterator, bool> result =
37      resource_map_.insert(ResourceMap::value_type(handle, resource_tracker));
38  EXPECT_TRUE(result.second);
39  result.first->second->AddRef();
40  return handle;
41}
42
43void FakeResourceManager::AddRef(PP_Resource handle) {
44  AUTO_LOCK(lock_);
45  ResourceMap::iterator iter = resource_map_.find(handle);
46  ASSERT_NE(resource_map_.end(), iter) << "AddRefing unknown resource "
47                                       << handle;
48
49  FakeResourceTracker* resource_tracker = iter->second;
50  EXPECT_LT(0, resource_tracker->ref_count()) << "AddRefing freed resource "
51                                              << resource_tracker->classname()
52                                              << "(" << handle
53                                              << "), created at "
54                                              << resource_tracker->file() << ":"
55                                              << resource_tracker->line();
56  resource_tracker->AddRef();
57}
58
59void FakeResourceManager::Release(PP_Resource handle) {
60  if (handle == 0)
61    return;
62
63  sdk_util::AutoLock lock(lock_);
64  ResourceMap::iterator iter = resource_map_.find(handle);
65  ASSERT_NE(resource_map_.end(), iter) << "Releasing unknown resource "
66                                       << handle;
67
68  FakeResourceTracker* resource_tracker = iter->second;
69  EXPECT_LT(0, resource_tracker->ref_count()) << "Releasing freed resource "
70                                              << resource_tracker->classname()
71                                              << "(" << handle
72                                              << "), created at "
73                                              << resource_tracker->file() << ":"
74                                              << resource_tracker->line();
75  resource_tracker->Release();
76  // It's OK to access the tracker when its refcount is zero; it doesn't
77  // actually destroy the object until the manager is destroyed.
78  if (resource_tracker->ref_count() == 0) {
79    // Remove the resource from this tracker.
80    FakeResource* resource = resource_tracker->Pass();
81    // Release the lock before we call Destroy; resources can call
82    // FakeResourceManager::Release(), which will deadlock if we are already
83    // holding the lock.
84    lock.Unlock();
85
86    resource->Destroy();
87    delete resource;
88  }
89}
90
91FakeResourceTracker* FakeResourceManager::Get(PP_Resource handle,
92                                              bool not_found_ok) {
93  AUTO_LOCK(lock_);
94  ResourceMap::iterator iter = resource_map_.find(handle);
95  if (iter == resource_map_.end()) {
96    if (!not_found_ok) {
97      // Can't use FAIL() because it tries to return void.
98      EXPECT_TRUE(false) << "Trying to get resource " << handle
99                         << " that doesn't exist!";
100    }
101
102    return NULL;
103  }
104
105  FakeResourceTracker* resource_tracker = iter->second;
106  EXPECT_LT(0, resource_tracker->ref_count()) << "Accessing freed resource "
107                                              << resource_tracker->classname()
108                                              << "(" << handle
109                                              << "), created at "
110                                              << resource_tracker->file() << ":"
111                                              << resource_tracker->line();
112
113  return iter->second;
114}
115
116FakeResourceTracker::FakeResourceTracker(FakeResource* resource,
117                                         const char* classname,
118                                         const char* file,
119                                         int line)
120    : resource_(resource),
121      classname_(classname),
122      file_(file),
123      line_(line),
124      ref_count_(0) {}
125
126FakeResourceTracker::~FakeResourceTracker() { delete resource_; }
127
128bool FakeResourceTracker::CheckType(const char* other_classname) const {
129  if (strcmp(other_classname, classname_) != 0) {
130    // Repeat the expectation, just to print out a nice error message before we
131    // crash. :)
132    EXPECT_STREQ(classname_, other_classname);
133    return false;
134  }
135
136  return true;
137}
138