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 32 namespace talk_base { 33 34 /////////////////////////////////////////////////////////////////////////////// 35 // SignalThread 36 /////////////////////////////////////////////////////////////////////////////// 37 38 SignalThread::SignalThread() 39 : main_(Thread::Current()), 40 worker_(this), 41 state_(kInit), 42 refcount_(1) { 43 main_->SignalQueueDestroyed.connect(this, 44 &SignalThread::OnMainThreadDestroyed); 45 worker_.SetName("SignalThread", this); 46 } 47 48 SignalThread::~SignalThread() { 49 ASSERT(refcount_ == 0); 50 } 51 52 bool SignalThread::SetName(const std::string& name, const void* obj) { 53 EnterExit ee(this); 54 ASSERT(main_->IsCurrent()); 55 ASSERT(kInit == state_); 56 return worker_.SetName(name, obj); 57 } 58 59 bool SignalThread::SetPriority(ThreadPriority priority) { 60 EnterExit ee(this); 61 ASSERT(main_->IsCurrent()); 62 ASSERT(kInit == state_); 63 return worker_.SetPriority(priority); 64 } 65 66 void SignalThread::Start() { 67 EnterExit ee(this); 68 ASSERT(main_->IsCurrent()); 69 if (kInit == state_ || kComplete == state_) { 70 state_ = kRunning; 71 OnWorkStart(); 72 worker_.Start(); 73 } else { 74 ASSERT(false); 75 } 76 } 77 78 void SignalThread::Destroy(bool wait) { 79 EnterExit ee(this); 80 ASSERT(main_->IsCurrent()); 81 if ((kInit == state_) || (kComplete == state_)) { 82 refcount_--; 83 } else if (kRunning == state_ || kReleasing == state_) { 84 state_ = kStopping; 85 // OnWorkStop() must follow Quit(), so that when the thread wakes up due to 86 // OWS(), ContinueWork() will return false. 87 worker_.Quit(); 88 OnWorkStop(); 89 if (wait) { 90 // Release the thread's lock so that it can return from ::Run. 91 cs_.Leave(); 92 worker_.Stop(); 93 cs_.Enter(); 94 refcount_--; 95 } 96 } else { 97 ASSERT(false); 98 } 99 } 100 101 void SignalThread::Release() { 102 EnterExit ee(this); 103 ASSERT(main_->IsCurrent()); 104 if (kComplete == state_) { 105 refcount_--; 106 } else if (kRunning == state_) { 107 state_ = kReleasing; 108 } else { 109 // if (kInit == state_) use Destroy() 110 ASSERT(false); 111 } 112 } 113 114 bool SignalThread::ContinueWork() { 115 EnterExit ee(this); 116 ASSERT(worker_.IsCurrent()); 117 return worker_.ProcessMessages(0); 118 } 119 120 void SignalThread::OnMessage(Message *msg) { 121 EnterExit ee(this); 122 if (ST_MSG_WORKER_DONE == msg->message_id) { 123 ASSERT(main_->IsCurrent()); 124 OnWorkDone(); 125 bool do_delete = false; 126 if (kRunning == state_) { 127 state_ = kComplete; 128 } else { 129 do_delete = true; 130 } 131 if (kStopping != state_) { 132 // Before signaling that the work is done, make sure that the worker 133 // thread actually is done. We got here because DoWork() finished and 134 // Run() posted the ST_MSG_WORKER_DONE message. This means the worker 135 // thread is about to go away anyway, but sometimes it doesn't actually 136 // finish before SignalWorkDone is processed, and for a reusable 137 // SignalThread this makes an assert in thread.cc fire. 138 // 139 // Calling Stop() on the worker ensures that the OS thread that underlies 140 // the worker will finish, and will be set to NULL, enabling us to call 141 // Start() again. 142 worker_.Stop(); 143 SignalWorkDone(this); 144 } 145 if (do_delete) { 146 refcount_--; 147 } 148 } 149 } 150 151 void SignalThread::Run() { 152 DoWork(); 153 { 154 EnterExit ee(this); 155 if (main_) { 156 main_->Post(this, ST_MSG_WORKER_DONE); 157 } 158 } 159 } 160 161 void SignalThread::OnMainThreadDestroyed() { 162 EnterExit ee(this); 163 main_ = NULL; 164 } 165 166 } // namespace talk_base 167