| // 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. |
| |
| #ifndef PPAPI_UTILITY_THREAD_SAFE_THREAD_TRAITS_H_ |
| #define PPAPI_UTILITY_THREAD_SAFE_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_THREAD_SAFE_THREAD_TRAITS_H_ |