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