| Index: base/thread_collision_warner.h
|
| ===================================================================
|
| --- base/thread_collision_warner.h (revision 0)
|
| +++ base/thread_collision_warner.h (revision 0)
|
| @@ -0,0 +1,239 @@
|
| +// Copyright (c) 2008 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 THREAD_COLLISION_WARNER_H_
|
| +#define THREAD_COLLISION_WARNER_H_
|
| +
|
| +#include "base/atomicops.h"
|
| +#include "base/platform_thread.h"
|
| +
|
| +#include <memory>
|
| +
|
| +// A helper class used to mark/define critical section in a class and then
|
| +// install controls to check that those critical sections are not violated
|
| +
|
| +// Example: Queue implementation non thread-safe but still usable if clients
|
| +// are synchronized somehow.
|
| +//
|
| +// In this case the macro D_SCOPED_BOOK_CRITICAL_SECTION has to be
|
| +// used, it checks that if a thread is inside the push/pop then
|
| +// noone else is still inside the pop/push
|
| +//
|
| +// class NonThreadSafeQueue {
|
| +// public:
|
| +// ...
|
| +// void push(int) { D_SCOPED_BOOK_CRITICAL_SECTION(push_pop_); ... }
|
| +// int pop() { D_SCOPED_BOOK_CRITICAL_SECTION(push_pop_); ... }
|
| +// ...
|
| +// private:
|
| +// D_DEFINE_CRITICAL_SECTION(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 D_SCOPED_RECURSIVE_BOOK_CRITICAL_SECTION
|
| +// has to be used, it checks that if a thread is inside the push/pop
|
| +// then noone else is still inside the pop/push
|
| +//
|
| +// class NonThreadSafeQueue {
|
| +// public:
|
| +// ...
|
| +// void push(int) {
|
| +// D_SCOPED_RECURSIVE_BOOK_CRITICAL_SECTION(push_pop_);
|
| +// ...
|
| +// }
|
| +// int pop() {
|
| +// D_SCOPED_RECURSIVE_BOOK_CRITICAL_SECTION(push_pop_);
|
| +// bar();
|
| +// ...
|
| +// }
|
| +// void bar() { D_SCOPED_RECURSIVE_BOOK_CRITICAL_SECTION(push_pop_); ... }
|
| +// ...
|
| +// private:
|
| +// D_DEFINE_CRITICAL_SECTION(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 D_BOOK_CRITICAL_SECTION 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) { D_BOOK_CRITICAL_SECTION(push_pop_); ... }
|
| +// int pop() { D_BOOK_CRITICAL_SECTION(push_pop_); ... }
|
| +// ...
|
| +// private:
|
| +// D_DEFINE_CRITICAL_SECTION(push_pop_);
|
| +// };
|
| +//
|
| +//
|
| +//
|
| +// Example: Class that has to be contructed/destroyed on same thread, it has
|
| +// a "shareable" method (with external syncronization) and a not
|
| +// shareable method (even with external syncronization).
|
| +//
|
| +// In this case 3 Critical sections have to be defined
|
| +//
|
| +// class ExoticClass {
|
| +// public:
|
| +// ExoticClass() { D_BOOK_CRITICAL_SECTION(ctor_dtor_); ... }
|
| +// ~ExoticClass() { D_BOOK_CRITICAL_SECTION(ctor_dtor_); ... }
|
| +//
|
| +// void Sharable() { D_SCOPED_BOOK_CRITICAL_SECTION(sharable_section_); ... }
|
| +// void NotSharable() { D_BOOK_CRITICAL_SECTION(not_sharable_section_); ... }
|
| +// ...
|
| +// private:
|
| +// D_DEFINE_CRITICAL_SECTION(ctor_dtor_);
|
| +// D_DEFINE_CRITICAL_SECTION(sharable_section_);
|
| +// D_DEFINE_CRITICAL_SECTION(not_sharable_section_);
|
| +// };
|
| +
|
| +
|
| +#ifndef NDEBUG
|
| +
|
| +#define D_DEFINE_CRITICAL_SECTION(obj) \
|
| + mutable base::ThreadCollisionWarner obj
|
| +#define D_SCOPED_BOOK_CRITICAL_SECTION(obj) \
|
| + base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj)
|
| +#define D_SCOPED_RECURSIVE_BOOK_CRITICAL_SECTION(obj) \
|
| + base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj)
|
| +#define D_BOOK_CRITICAL_SECTION(obj) \
|
| + base::ThreadCollisionWarner::Check check_##obj(&obj)
|
| +
|
| +#else
|
| +
|
| +#define D_DEFINE_CRITICAL_SECTION(obj)
|
| +#define D_SCOPED_BOOK_CRITICAL_SECTION(obj)
|
| +#define D_BOOK_CRITICAL_SECTION(obj)
|
| +
|
| +#endif
|
| +
|
| +namespace base {
|
| +
|
| +// 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 : AsserterBase {
|
| + virtual ~DCheckAsserter() {}
|
| + virtual void warn();
|
| +};
|
| +
|
| +class ThreadCollisionWarner {
|
| + public:
|
| + // The parameter asserter is there only for test purpose
|
| + ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
|
| + : valid_thread_id_(0),
|
| + counter_(0),
|
| + asserter_(asserter) {}
|
| +
|
| + ~ThreadCollisionWarner() {
|
| + delete asserter_;
|
| + }
|
| +
|
| + // This class is meant to be used through the macro D_BOOK_CRITICAL_SECTION
|
| + // 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)
|
| + : warner_(warner) {
|
| + warner_->EnterSelf();
|
| + }
|
| +
|
| + ~Check() {}
|
| +
|
| + private:
|
| + ThreadCollisionWarner* warner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Check);
|
| + };
|
| +
|
| + // This class is meant to be used through the macro
|
| + // D_SCOPED_BOOK_CRITICAL_SECTION
|
| + class ScopedCheck {
|
| + public:
|
| + explicit ScopedCheck(ThreadCollisionWarner* warner)
|
| + : warner_(warner) {
|
| + warner_->Enter();
|
| + }
|
| +
|
| + ~ScopedCheck() {
|
| + warner_->Leave();
|
| + }
|
| +
|
| + private:
|
| + ThreadCollisionWarner* warner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
|
| + };
|
| +
|
| + // This class is meant to be used through the macro
|
| + // D_SCOPED_RECURSIVE_BOOK_CRITICAL_SECTION
|
| + class ScopedRecursiveCheck {
|
| + public:
|
| + explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
|
| + : warner_(warner) {
|
| + warner_->EnterSelf();
|
| + }
|
| +
|
| + ~ScopedRecursiveCheck() {
|
| + warner_->Leave();
|
| + }
|
| +
|
| + private:
|
| + ThreadCollisionWarner* warner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck);
|
| + };
|
| +
|
| + 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 int 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 subtle::Atomic32 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_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner);
|
| +};
|
| +
|
| +} // namespace base
|
| +
|
| +#endif // THREAD_COLLISION_WARNER_H_
|
| +
|
|
|