|  | // 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 STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_ | 
|  | #define STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_ | 
|  |  | 
|  | #include "starboard/atomic.h" | 
|  |  | 
|  | // A helper class alongside macros to be used to verify assumptions about thread | 
|  | // safety of a class. | 
|  | // | 
|  | // Example: Queue implementation non thread-safe but still usable if clients | 
|  | //          are synchronized somehow. | 
|  | // | 
|  | //          In this case the macro DFAKE_SCOPED_LOCK has to be | 
|  | //          used, it checks that if a thread is inside the push/pop then | 
|  | //          no one else is still inside the pop/push | 
|  | // | 
|  | // class NonThreadSafeQueue { | 
|  | //  public: | 
|  | //   ... | 
|  | //   void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } | 
|  | //   int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } | 
|  | //   ... | 
|  | //  private: | 
|  | //   DFAKE_MUTEX(push_pop_); | 
|  | // }; | 
|  | // | 
|  | // | 
|  | // Example: Queue implementation non thread-safe but still usable if clients | 
|  | //          are synchronized somehow, it calls a method to "protect" from | 
|  | //          a "protected" method | 
|  | // | 
|  | //          In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK | 
|  | //          has to be used, it checks that if a thread is inside the push/pop | 
|  | //          then no one else is still inside the pop/push | 
|  | // | 
|  | // class NonThreadSafeQueue { | 
|  | //  public: | 
|  | //   void push(int) { | 
|  | //     DFAKE_SCOPED_LOCK(push_pop_); | 
|  | //     ... | 
|  | //   } | 
|  | //   int pop() { | 
|  | //     DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); | 
|  | //     bar(); | 
|  | //     ... | 
|  | //   } | 
|  | //   void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } | 
|  | //   ... | 
|  | //  private: | 
|  | //   DFAKE_MUTEX(push_pop_); | 
|  | // }; | 
|  | // | 
|  | // | 
|  | // Example: Queue implementation not usable even if clients are synchronized, | 
|  | //          so only one thread in the class life cycle can use the two members | 
|  | //          push/pop. | 
|  | // | 
|  | //          In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the | 
|  | //          specified | 
|  | //          critical section the first time a thread enters push or pop, from | 
|  | //          that time on only that thread is allowed to execute push or pop. | 
|  | // | 
|  | // class NonThreadSafeQueue { | 
|  | //  public: | 
|  | //   ... | 
|  | //   void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } | 
|  | //   int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } | 
|  | //   ... | 
|  | //  private: | 
|  | //   DFAKE_MUTEX(push_pop_); | 
|  | // }; | 
|  | // | 
|  | // | 
|  | // Example: Class that has to be constructed/destroyed on same thread, it has | 
|  | //          a "shareable" method (with external synchronization) and a not | 
|  | //          shareable method (even with external synchronization). | 
|  | // | 
|  | //          In this case 3 Critical sections have to be defined | 
|  | // | 
|  | // class ExoticClass { | 
|  | //  public: | 
|  | //   ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } | 
|  | //   ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } | 
|  | // | 
|  | //   void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } | 
|  | //   void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } | 
|  | //   ... | 
|  | //  private: | 
|  | //   DFAKE_MUTEX(ctor_dtor_); | 
|  | //   DFAKE_MUTEX(shareable_section_); | 
|  | // }; | 
|  |  | 
|  | #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) | 
|  |  | 
|  | // Defines a class member that acts like a mutex. It is used only as a | 
|  | // verification tool. | 
|  | #define DFAKE_MUTEX(obj) mutable starboard::ThreadCollisionWarner obj | 
|  | // Asserts the call is never called simultaneously in two threads. Used at | 
|  | // member function scope. | 
|  | #define DFAKE_SCOPED_LOCK(obj) \ | 
|  | starboard::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) | 
|  | // Asserts the call is never called simultaneously in two threads. Used at | 
|  | // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. | 
|  | #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ | 
|  | starboard::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) | 
|  | // Asserts the code is always executed in the same thread. | 
|  | #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ | 
|  | starboard::ThreadCollisionWarner::Check check_##obj(&obj) | 
|  |  | 
|  | #else | 
|  |  | 
|  | #define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj | 
|  | #define DFAKE_SCOPED_LOCK(obj) ((void)0) | 
|  | #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) | 
|  | #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) | 
|  |  | 
|  | #endif | 
|  |  | 
|  | namespace starboard { | 
|  |  | 
|  | // The class ThreadCollisionWarner uses an Asserter to notify the collision | 
|  | // AsserterBase is the interfaces and DCheckAsserter is the default asserter | 
|  | // used. During the unit tests is used another class that doesn't "DCHECK" | 
|  | // in case of collision (check thread_collision_warner_unittests.cc) | 
|  | struct AsserterBase { | 
|  | virtual ~AsserterBase(); | 
|  | virtual void warn() = 0; | 
|  | }; | 
|  |  | 
|  | struct DCheckAsserter : public AsserterBase { | 
|  | virtual ~DCheckAsserter(); | 
|  | virtual void warn(); | 
|  | }; | 
|  |  | 
|  | class ThreadCollisionWarner { | 
|  | public: | 
|  | // The parameter asserter is there only for test purpose | 
|  | explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()); | 
|  |  | 
|  | ~ThreadCollisionWarner(); | 
|  |  | 
|  | // This class is meant to be used through the macro | 
|  | // DFAKE_SCOPED_LOCK_THREAD_LOCKED | 
|  | // it doesn't leave the critical section, as opposed to ScopedCheck, | 
|  | // because the critical section being pinned is allowed to be used only | 
|  | // from one thread | 
|  | class Check { | 
|  | public: | 
|  | explicit Check(ThreadCollisionWarner* warner); | 
|  |  | 
|  | ~Check(); | 
|  |  | 
|  | private: | 
|  | ThreadCollisionWarner* warner_; | 
|  | }; | 
|  |  | 
|  | // This class is meant to be used through the macro | 
|  | // DFAKE_SCOPED_LOCK | 
|  | class ScopedCheck { | 
|  | public: | 
|  | explicit ScopedCheck(ThreadCollisionWarner* warner) : warner_(warner) { | 
|  | warner_->Enter(); | 
|  | } | 
|  |  | 
|  | ~ScopedCheck() { warner_->Leave(); } | 
|  |  | 
|  | private: | 
|  | ThreadCollisionWarner* warner_; | 
|  | }; | 
|  |  | 
|  | // This class is meant to be used through the macro | 
|  | // DFAKE_SCOPED_RECURSIVE_LOCK | 
|  | class ScopedRecursiveCheck { | 
|  | public: | 
|  | explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) | 
|  | : warner_(warner) { | 
|  | warner_->EnterSelf(); | 
|  | } | 
|  |  | 
|  | ~ScopedRecursiveCheck() { warner_->Leave(); } | 
|  |  | 
|  | private: | 
|  | ThreadCollisionWarner* warner_; | 
|  | }; | 
|  |  | 
|  | private: | 
|  | // This method stores the current thread identifier and does a DCHECK | 
|  | // if a another thread has already done it, it is safe if same thread | 
|  | // calls this multiple time (recursion allowed). | 
|  | void EnterSelf(); | 
|  |  | 
|  | // Same as EnterSelf but recursion is not allowed. | 
|  | void Enter(); | 
|  |  | 
|  | // Removes the thread_id stored in order to allow other threads to | 
|  | // call EnterSelf or Enter. | 
|  | void Leave(); | 
|  |  | 
|  | // This stores the thread id that is inside the critical section, if the | 
|  | // value is 0 then no thread is inside. | 
|  | volatile SbAtomic32 valid_thread_id_; | 
|  |  | 
|  | // Counter to trace how many time a critical section was "pinned" | 
|  | // (when allowed) in order to unpin it when counter_ reaches 0. | 
|  | volatile SbAtomic32 counter_; | 
|  |  | 
|  | // Here only for class unit tests purpose, during the test I need to not | 
|  | // DCHECK but notify the collision with something else. | 
|  | AsserterBase* asserter_; | 
|  | }; | 
|  |  | 
|  | }  // namespace starboard | 
|  |  | 
|  | #endif  // STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_ |