Add a facility to the Thread class to catch blocking regressions.

This facility should be used in methods that run on known threads
(e.g. signaling, worker) and do not have blocking thread syncronization
operations via the Thread class such as Invoke, Sleep, etc.

This is a reland of an already reviewed cl (r6679) that got reverted by mistake.

TBR=xians@google.com,tommi@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/21889004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6682 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrike@webrtc.org 2014-07-14 21:42:39 +00:00
parent b038c72369
commit 1b84116417
2 changed files with 58 additions and 1 deletions

View File

@ -142,6 +142,16 @@ struct ThreadInit {
Runnable* runnable; Runnable* runnable;
}; };
Thread::ScopedDisallowBlockingCalls::ScopedDisallowBlockingCalls()
: thread_(Thread::Current()),
previous_state_(thread_->SetAllowBlockingCalls(false)) {
}
Thread::ScopedDisallowBlockingCalls::~ScopedDisallowBlockingCalls() {
ASSERT(thread_->IsCurrent());
thread_->SetAllowBlockingCalls(previous_state_);
}
Thread::Thread(SocketServer* ss) Thread::Thread(SocketServer* ss)
: MessageQueue(ss), : MessageQueue(ss),
priority_(PRIORITY_NORMAL), priority_(PRIORITY_NORMAL),
@ -150,7 +160,8 @@ Thread::Thread(SocketServer* ss)
thread_(NULL), thread_(NULL),
thread_id_(0), thread_id_(0),
#endif #endif
owned_(true) { owned_(true),
blocking_calls_allowed_(true) {
SetName("Thread", this); // default name SetName("Thread", this); // default name
} }
@ -160,6 +171,8 @@ Thread::~Thread() {
} }
bool Thread::SleepMs(int milliseconds) { bool Thread::SleepMs(int milliseconds) {
AssertBlockingIsAllowedOnCurrentThread();
#ifdef WIN32 #ifdef WIN32
::Sleep(milliseconds); ::Sleep(milliseconds);
return true; return true;
@ -293,6 +306,8 @@ bool Thread::Start(Runnable* runnable) {
} }
void Thread::Join() { void Thread::Join() {
AssertBlockingIsAllowedOnCurrentThread();
if (running()) { if (running()) {
ASSERT(!IsCurrent()); ASSERT(!IsCurrent());
#if defined(WIN32) #if defined(WIN32)
@ -308,6 +323,21 @@ void Thread::Join() {
} }
} }
bool Thread::SetAllowBlockingCalls(bool allow) {
ASSERT(IsCurrent());
bool previous = blocking_calls_allowed_;
blocking_calls_allowed_ = allow;
return previous;
}
// static
void Thread::AssertBlockingIsAllowedOnCurrentThread() {
#ifdef _DEBUG
Thread* current = Thread::Current();
ASSERT(!current || current->blocking_calls_allowed_);
#endif
}
#ifdef WIN32 #ifdef WIN32
// As seen on MSDN. // As seen on MSDN.
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx
@ -374,6 +404,8 @@ void Thread::Stop() {
} }
void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
AssertBlockingIsAllowedOnCurrentThread();
if (fStop_) if (fStop_)
return; return;

View File

@ -125,6 +125,19 @@ class Thread : public MessageQueue {
static Thread* Current(); static Thread* Current();
// Used to catch performance regressions. Use this to disallow blocking calls
// (Invoke) for a given scope. If a synchronous call is made while this is in
// effect, an assert will be triggered.
// Note that this is a single threaded class.
class ScopedDisallowBlockingCalls {
public:
ScopedDisallowBlockingCalls();
~ScopedDisallowBlockingCalls();
private:
Thread* const thread_;
const bool previous_state_;
};
bool IsCurrent() const { bool IsCurrent() const {
return Current() == this; return Current() == this;
} }
@ -165,8 +178,11 @@ class Thread : public MessageQueue {
// Uses Send() internally, which blocks the current thread until execution // Uses Send() internally, which blocks the current thread until execution
// is complete. // is complete.
// Ex: bool result = thread.Invoke<bool>(&MyFunctionReturningBool); // Ex: bool result = thread.Invoke<bool>(&MyFunctionReturningBool);
// NOTE: This function can only be called when synchronous calls are allowed.
// See ScopedDisallowBlockingCalls for details.
template <class ReturnT, class FunctorT> template <class ReturnT, class FunctorT>
ReturnT Invoke(const FunctorT& functor) { ReturnT Invoke(const FunctorT& functor) {
AssertBlockingIsAllowedOnCurrentThread();
FunctorMessageHandler<ReturnT, FunctorT> handler(functor); FunctorMessageHandler<ReturnT, FunctorT> handler(functor);
Send(&handler); Send(&handler);
return handler.result(); return handler.result();
@ -229,6 +245,14 @@ class Thread : public MessageQueue {
// Blocks the calling thread until this thread has terminated. // Blocks the calling thread until this thread has terminated.
void Join(); void Join();
// Sets the per-thread allow-blocking-calls flag and returns the previous
// value.
bool SetAllowBlockingCalls(bool allow);
static void AssertBlockingIsAllowedOnCurrentThread();
friend class ScopedDisallowBlockingCalls;
private: private:
static void *PreRun(void *pv); static void *PreRun(void *pv);
@ -255,6 +279,7 @@ class Thread : public MessageQueue {
#endif #endif
bool owned_; bool owned_;
bool blocking_calls_allowed_; // By default set to |true|.
friend class ThreadManager; friend class ThreadManager;