From 1b84116417d5b5809482cd0b0d9dd4af54668508 Mon Sep 17 00:00:00 2001 From: "henrike@webrtc.org" Date: Mon, 14 Jul 2014 21:42:39 +0000 Subject: [PATCH] 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 --- talk/base/thread.cc | 34 +++++++++++++++++++++++++++++++++- talk/base/thread.h | 25 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/talk/base/thread.cc b/talk/base/thread.cc index 87e4ffff6..3acd9a813 100644 --- a/talk/base/thread.cc +++ b/talk/base/thread.cc @@ -142,6 +142,16 @@ struct ThreadInit { 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) : MessageQueue(ss), priority_(PRIORITY_NORMAL), @@ -150,7 +160,8 @@ Thread::Thread(SocketServer* ss) thread_(NULL), thread_id_(0), #endif - owned_(true) { + owned_(true), + blocking_calls_allowed_(true) { SetName("Thread", this); // default name } @@ -160,6 +171,8 @@ Thread::~Thread() { } bool Thread::SleepMs(int milliseconds) { + AssertBlockingIsAllowedOnCurrentThread(); + #ifdef WIN32 ::Sleep(milliseconds); return true; @@ -293,6 +306,8 @@ bool Thread::Start(Runnable* runnable) { } void Thread::Join() { + AssertBlockingIsAllowedOnCurrentThread(); + if (running()) { ASSERT(!IsCurrent()); #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 // As seen on MSDN. // 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) { + AssertBlockingIsAllowedOnCurrentThread(); + if (fStop_) return; diff --git a/talk/base/thread.h b/talk/base/thread.h index 4cbf721fa..ef9786293 100644 --- a/talk/base/thread.h +++ b/talk/base/thread.h @@ -125,6 +125,19 @@ class Thread : public MessageQueue { 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 { return Current() == this; } @@ -165,8 +178,11 @@ class Thread : public MessageQueue { // Uses Send() internally, which blocks the current thread until execution // is complete. // Ex: bool result = thread.Invoke(&MyFunctionReturningBool); + // NOTE: This function can only be called when synchronous calls are allowed. + // See ScopedDisallowBlockingCalls for details. template ReturnT Invoke(const FunctorT& functor) { + AssertBlockingIsAllowedOnCurrentThread(); FunctorMessageHandler handler(functor); Send(&handler); return handler.result(); @@ -229,6 +245,14 @@ class Thread : public MessageQueue { // Blocks the calling thread until this thread has terminated. 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: static void *PreRun(void *pv); @@ -255,6 +279,7 @@ class Thread : public MessageQueue { #endif bool owned_; + bool blocking_calls_allowed_; // By default set to |true|. friend class ThreadManager;