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
SignalThread()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
~SignalThread()48 SignalThread::~SignalThread() {
49 ASSERT(refcount_ == 0);
50 }
51
SetName(const std::string & name,const void * obj)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
SetPriority(ThreadPriority priority)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
Start()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
Destroy(bool wait)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
Release()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
ContinueWork()114 bool SignalThread::ContinueWork() {
115 EnterExit ee(this);
116 ASSERT(worker_.IsCurrent());
117 return worker_.ProcessMessages(0);
118 }
119
OnMessage(Message * msg)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
Run()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
OnMainThreadDestroyed()161 void SignalThread::OnMainThreadDestroyed() {
162 EnterExit ee(this);
163 main_ = NULL;
164 }
165
166 } // namespace talk_base
167