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
diff --git a/talk/base/thread.cc b/talk/base/thread.cc
index 87e4fff..3acd9a8 100644
--- a/talk/base/thread.cc
+++ b/talk/base/thread.cc
@@ -142,6 +142,16 @@
   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_(NULL),
       thread_id_(0),
 #endif
-      owned_(true) {
+      owned_(true),
+      blocking_calls_allowed_(true) {
   SetName("Thread", this);  // default name
 }
 
@@ -160,6 +171,8 @@
 }
 
 bool Thread::SleepMs(int milliseconds) {
+  AssertBlockingIsAllowedOnCurrentThread();
+
 #ifdef WIN32
   ::Sleep(milliseconds);
   return true;
@@ -293,6 +306,8 @@
 }
 
 void Thread::Join() {
+  AssertBlockingIsAllowedOnCurrentThread();
+
   if (running()) {
     ASSERT(!IsCurrent());
 #if defined(WIN32)
@@ -308,6 +323,21 @@
   }
 }
 
+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::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
+  AssertBlockingIsAllowedOnCurrentThread();
+
   if (fStop_)
     return;
 
diff --git a/talk/base/thread.h b/talk/base/thread.h
index 4cbf721..ef97862 100644
--- a/talk/base/thread.h
+++ b/talk/base/thread.h
@@ -125,6 +125,19 @@
 
   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 @@
   // Uses Send() internally, which blocks the current thread until execution
   // is complete.
   // 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>
   ReturnT Invoke(const FunctorT& functor) {
+    AssertBlockingIsAllowedOnCurrentThread();
     FunctorMessageHandler<ReturnT, FunctorT> handler(functor);
     Send(&handler);
     return handler.result();
@@ -229,6 +245,14 @@
   // 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 @@
 #endif
 
   bool owned_;
+  bool blocking_calls_allowed_;  // By default set to |true|.
 
   friend class ThreadManager;