1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PPAPI_UTILITY_COMPLETION_CALLBACK_FACTORY_THREAD_TRAITS_H_
#define PPAPI_UTILITY_COMPLETION_CALLBACK_FACTORY_THREAD_TRAITS_H_
#include <stdint.h>
#include "ppapi/cpp/logging.h"
#include "ppapi/cpp/module.h"
#include "ppapi/utility/threading/lock.h"
/// @file
/// Defines the traits structures for thread-safety of a completion callback
/// factory. We provide thread-safe and non-thread-safe version. The thread-safe
/// version is always correct (if you follow the thread usage rules of the
/// callback factory), but if you know your object will only be used on one
/// thread, you can uses the non-thread-safe version.
///
/// The traits defines three nested classes to perform reference counting,
/// locks, and scoped locking.
namespace pp {
/// The thread-safe version of thread traits. Using this class as the "traits"
/// template argument to a completion callback factory will make it "somewhat
/// thread-friendly." It will allow you to create completion callbacks from
/// background threads and post them to another thread to run.
///
/// Care still must be taken to ensure that the completion callbacks are
/// executed on the same thread that the factory is destroyed on to avoid a
/// race on destruction.
///
/// Implementation note: this uses a lock instead of atomic add instructions.
/// The number of platforms we need to support right now makes atomic
/// operations unwieldy for this case that we don't actually use that often.
/// As a further optimization, we can add support for this later.
class ThreadSafeThreadTraits {
public:
class RefCount {
public:
/// Default constructor. In debug mode, this checks that the object is being
/// created on the main thread.
RefCount() : ref_(0) {
}
/// AddRef() increments the reference counter.
///
/// @return An int32_t with the incremented reference counter.
int32_t AddRef() {
AutoLock lock(lock_);
return ++ref_;
}
/// Release() decrements the reference counter.
///
/// @return An int32_t with the decremeneted reference counter.
int32_t Release() {
AutoLock lock(lock_);
PP_DCHECK(ref_ > 0);
return --ref_;
}
private:
Lock lock_;
int32_t ref_;
};
typedef pp::Lock Lock;
typedef pp::AutoLock AutoLock;
};
/// The non-thread-safe version of thread traits. Using this class as the
/// "traits" template argument to a completion callback factory will make it
/// not thread-safe but with potential extra performance.
class NonThreadSafeThreadTraits {
public:
/// A simple reference counter that is not thread-safe.
///
/// <strong>Note:</strong> in Debug mode, it checks that it is either called
/// on the main thread, or always called on another thread.
class RefCount {
public:
/// Default constructor. In debug mode, this checks that the object is being
/// created on the main thread.
RefCount() : ref_(0) {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
is_main_thread_ = Module::Get()->core()->IsMainThread();
#endif
}
/// Destructor.
~RefCount() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
}
/// AddRef() increments the reference counter.
///
/// @return An int32_t with the incremented reference counter.
int32_t AddRef() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
return ++ref_;
}
/// Release() decrements the reference counter.
///
/// @return An int32_t with the decremeneted reference counter.
int32_t Release() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
return --ref_;
}
private:
int32_t ref_;
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
bool is_main_thread_;
#endif
};
/// A simple object that acts like a lock but does nothing.
///
/// <strong>Note:</strong> in Debug mode, it checks that it is either
/// called on the main thread, or always called on another thread. It also
/// asserts that the caller does not recursively lock.
class Lock {
public:
Lock() {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
is_main_thread_ = Module::Get()->core()->IsMainThread();
lock_held_ = false;
#endif
}
~Lock() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
}
/// Acquires the fake "lock". This does nothing except perform checks in
/// debug mode.
void Acquire() {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
PP_DCHECK(!lock_held_);
lock_held_ = true;
#endif
}
/// Releases the fake "lock". This does nothing except perform checks in
/// debug mode.
void Release() {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
PP_DCHECK(lock_held_);
lock_held_ = false;
#endif
}
private:
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
bool is_main_thread_;
bool lock_held_;
#endif
};
class AutoLock {
public:
explicit AutoLock(Lock& lock) : lock_(lock) {
lock_.Acquire();
}
~AutoLock() {
lock_.Release();
}
private:
Lock& lock_;
};
};
} // namespace pp
#endif // PPAPI_UTILITY_COMPLETION_CALLBACK_FACTORY_THREAD_TRAITS_H_
|