1/* 2 * libjingle 3 * Copyright 2004--2009, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/base/signalthread.h" 29 30#include "talk/base/common.h" 31 32namespace talk_base { 33 34/////////////////////////////////////////////////////////////////////////////// 35// SignalThread 36/////////////////////////////////////////////////////////////////////////////// 37 38SignalThread::SignalThread() : main_(Thread::Current()), state_(kInit) { 39 main_->SignalQueueDestroyed.connect(this, 40 &SignalThread::OnMainThreadDestroyed); 41 refcount_ = 1; 42 worker_.parent_ = this; 43 worker_.SetName("SignalThread", this); 44} 45 46SignalThread::~SignalThread() { 47 ASSERT(refcount_ == 0); 48} 49 50bool SignalThread::SetName(const std::string& name, const void* obj) { 51 EnterExit ee(this); 52 ASSERT(main_->IsCurrent()); 53 ASSERT(kInit == state_); 54 return worker_.SetName(name, obj); 55} 56 57bool SignalThread::SetPriority(ThreadPriority priority) { 58 EnterExit ee(this); 59 ASSERT(main_->IsCurrent()); 60 ASSERT(kInit == state_); 61 return worker_.SetPriority(priority); 62} 63 64void SignalThread::Start() { 65 EnterExit ee(this); 66 ASSERT(main_->IsCurrent()); 67 if (kInit == state_ || kComplete == state_) { 68 state_ = kRunning; 69 OnWorkStart(); 70 worker_.Start(); 71 } else { 72 ASSERT(false); 73 } 74} 75 76void SignalThread::Destroy(bool wait) { 77 EnterExit ee(this); 78 ASSERT(main_->IsCurrent()); 79 if ((kInit == state_) || (kComplete == state_)) { 80 refcount_--; 81 } else if (kRunning == state_ || kReleasing == state_) { 82 state_ = kStopping; 83 // OnWorkStop() must follow Quit(), so that when the thread wakes up due to 84 // OWS(), ContinueWork() will return false. 85 worker_.Quit(); 86 OnWorkStop(); 87 if (wait) { 88 // Release the thread's lock so that it can return from ::Run. 89 cs_.Leave(); 90 worker_.Stop(); 91 cs_.Enter(); 92 refcount_--; 93 } 94 } else { 95 ASSERT(false); 96 } 97} 98 99void SignalThread::Release() { 100 EnterExit ee(this); 101 ASSERT(main_->IsCurrent()); 102 if (kComplete == state_) { 103 refcount_--; 104 } else if (kRunning == state_) { 105 state_ = kReleasing; 106 } else { 107 // if (kInit == state_) use Destroy() 108 ASSERT(false); 109 } 110} 111 112bool SignalThread::ContinueWork() { 113 EnterExit ee(this); 114 ASSERT(worker_.IsCurrent()); 115 return worker_.ProcessMessages(0); 116} 117 118void SignalThread::OnMessage(Message *msg) { 119 EnterExit ee(this); 120 if (ST_MSG_WORKER_DONE == msg->message_id) { 121 ASSERT(main_->IsCurrent()); 122 OnWorkDone(); 123 bool do_delete = false; 124 if (kRunning == state_) { 125 state_ = kComplete; 126 } else { 127 do_delete = true; 128 } 129 if (kStopping != state_) { 130 // Before signaling that the work is done, make sure that the worker 131 // thread actually is done. We got here because DoWork() finished and 132 // Run() posted the ST_MSG_WORKER_DONE message. This means the worker 133 // thread is about to go away anyway, but sometimes it doesn't actually 134 // finish before SignalWorkDone is processed, and for a reusable 135 // SignalThread this makes an assert in thread.cc fire. 136 // 137 // Calling Stop() on the worker ensures that the OS thread that underlies 138 // the worker will finish, and will be set to NULL, enabling us to call 139 // Start() again. 140 worker_.Stop(); 141 SignalWorkDone(this); 142 } 143 if (do_delete) { 144 refcount_--; 145 } 146 } 147} 148 149void SignalThread::Run() { 150 DoWork(); 151 { 152 EnterExit ee(this); 153 if (main_) { 154 main_->Post(this, ST_MSG_WORKER_DONE); 155 } 156 } 157} 158 159void SignalThread::OnMainThreadDestroyed() { 160 EnterExit ee(this); 161 main_ = NULL; 162} 163 164} // namespace talk_base 165