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