1/* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include "webrtc/base/signalthread.h" 12 13#include "webrtc/base/common.h" 14 15namespace rtc { 16 17/////////////////////////////////////////////////////////////////////////////// 18// SignalThread 19/////////////////////////////////////////////////////////////////////////////// 20 21SignalThread::SignalThread() 22 : main_(Thread::Current()), 23 worker_(this), 24 state_(kInit), 25 refcount_(1) { 26 main_->SignalQueueDestroyed.connect(this, 27 &SignalThread::OnMainThreadDestroyed); 28 worker_.SetName("SignalThread", this); 29} 30 31SignalThread::~SignalThread() { 32 ASSERT(refcount_ == 0); 33} 34 35bool SignalThread::SetName(const std::string& name, const void* obj) { 36 EnterExit ee(this); 37 ASSERT(main_->IsCurrent()); 38 ASSERT(kInit == state_); 39 return worker_.SetName(name, obj); 40} 41 42void SignalThread::Start() { 43 EnterExit ee(this); 44 ASSERT(main_->IsCurrent()); 45 if (kInit == state_ || kComplete == state_) { 46 state_ = kRunning; 47 OnWorkStart(); 48 worker_.Start(); 49 } else { 50 ASSERT(false); 51 } 52} 53 54void SignalThread::Destroy(bool wait) { 55 EnterExit ee(this); 56 ASSERT(main_->IsCurrent()); 57 if ((kInit == state_) || (kComplete == state_)) { 58 refcount_--; 59 } else if (kRunning == state_ || kReleasing == state_) { 60 state_ = kStopping; 61 // OnWorkStop() must follow Quit(), so that when the thread wakes up due to 62 // OWS(), ContinueWork() will return false. 63 worker_.Quit(); 64 OnWorkStop(); 65 if (wait) { 66 // Release the thread's lock so that it can return from ::Run. 67 cs_.Leave(); 68 worker_.Stop(); 69 cs_.Enter(); 70 refcount_--; 71 } 72 } else { 73 ASSERT(false); 74 } 75} 76 77void SignalThread::Release() { 78 EnterExit ee(this); 79 ASSERT(main_->IsCurrent()); 80 if (kComplete == state_) { 81 refcount_--; 82 } else if (kRunning == state_) { 83 state_ = kReleasing; 84 } else { 85 // if (kInit == state_) use Destroy() 86 ASSERT(false); 87 } 88} 89 90bool SignalThread::ContinueWork() { 91 EnterExit ee(this); 92 ASSERT(worker_.IsCurrent()); 93 return worker_.ProcessMessages(0); 94} 95 96void SignalThread::OnMessage(Message *msg) { 97 EnterExit ee(this); 98 if (ST_MSG_WORKER_DONE == msg->message_id) { 99 ASSERT(main_->IsCurrent()); 100 OnWorkDone(); 101 bool do_delete = false; 102 if (kRunning == state_) { 103 state_ = kComplete; 104 } else { 105 do_delete = true; 106 } 107 if (kStopping != state_) { 108 // Before signaling that the work is done, make sure that the worker 109 // thread actually is done. We got here because DoWork() finished and 110 // Run() posted the ST_MSG_WORKER_DONE message. This means the worker 111 // thread is about to go away anyway, but sometimes it doesn't actually 112 // finish before SignalWorkDone is processed, and for a reusable 113 // SignalThread this makes an assert in thread.cc fire. 114 // 115 // Calling Stop() on the worker ensures that the OS thread that underlies 116 // the worker will finish, and will be set to NULL, enabling us to call 117 // Start() again. 118 worker_.Stop(); 119 SignalWorkDone(this); 120 } 121 if (do_delete) { 122 refcount_--; 123 } 124 } 125} 126 127SignalThread::Worker::~Worker() { 128 Stop(); 129} 130 131void SignalThread::Worker::Run() { 132 parent_->Run(); 133} 134 135void SignalThread::Run() { 136 DoWork(); 137 { 138 EnterExit ee(this); 139 if (main_) { 140 main_->Post(this, ST_MSG_WORKER_DONE); 141 } 142 } 143} 144 145void SignalThread::OnMainThreadDestroyed() { 146 EnterExit ee(this); 147 main_ = NULL; 148} 149 150} // namespace rtc 151