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