1/*
2 *  Copyright (c) 2011 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/system_wrappers/source/thread_win.h"
12
13#include <assert.h>
14#include <process.h>
15#include <stdio.h>
16#include <windows.h>
17
18#include "webrtc/system_wrappers/interface/trace.h"
19#include "webrtc/system_wrappers/source/set_thread_name_win.h"
20
21namespace webrtc {
22
23ThreadWindows::ThreadWindows(ThreadRunFunction func, ThreadObj obj,
24                             ThreadPriority prio, const char* thread_name)
25    : ThreadWrapper(),
26      run_function_(func),
27      obj_(obj),
28      alive_(false),
29      dead_(true),
30      do_not_close_handle_(false),
31      prio_(prio),
32      event_(NULL),
33      thread_(NULL),
34      id_(0),
35      name_(),
36      set_thread_name_(false) {
37  event_ = EventWrapper::Create();
38  critsect_stop_ = CriticalSectionWrapper::CreateCriticalSection();
39  if (thread_name != NULL) {
40    // Set the thread name to appear in the VS debugger.
41    set_thread_name_ = true;
42    strncpy(name_, thread_name, kThreadMaxNameLength);
43  }
44}
45
46ThreadWindows::~ThreadWindows() {
47#ifdef _DEBUG
48  assert(!alive_);
49#endif
50  if (thread_) {
51    CloseHandle(thread_);
52  }
53  if (event_) {
54    delete event_;
55  }
56  if (critsect_stop_) {
57    delete critsect_stop_;
58  }
59}
60
61uint32_t ThreadWrapper::GetThreadId() {
62  return GetCurrentThreadId();
63}
64
65unsigned int WINAPI ThreadWindows::StartThread(LPVOID lp_parameter) {
66  static_cast<ThreadWindows*>(lp_parameter)->Run();
67  return 0;
68}
69
70bool ThreadWindows::Start(unsigned int& thread_id) {
71  if (!run_function_) {
72    return false;
73  }
74  do_not_close_handle_ = false;
75
76  // Set stack size to 1M
77  thread_ = (HANDLE)_beginthreadex(NULL, 1024 * 1024, StartThread, (void*)this,
78                                   0, &thread_id);
79  if (thread_ == NULL) {
80    return false;
81  }
82  id_ = thread_id;
83  event_->Wait(INFINITE);
84
85  switch (prio_) {
86    case kLowPriority:
87      SetThreadPriority(thread_, THREAD_PRIORITY_BELOW_NORMAL);
88      break;
89    case kNormalPriority:
90      SetThreadPriority(thread_, THREAD_PRIORITY_NORMAL);
91      break;
92    case kHighPriority:
93      SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL);
94      break;
95    case kHighestPriority:
96      SetThreadPriority(thread_, THREAD_PRIORITY_HIGHEST);
97      break;
98    case kRealtimePriority:
99      SetThreadPriority(thread_, THREAD_PRIORITY_TIME_CRITICAL);
100      break;
101  };
102  return true;
103}
104
105bool ThreadWindows::SetAffinity(const int* processor_numbers,
106                                const unsigned int amount_of_processors) {
107  DWORD_PTR processor_bit_mask = 0;
108  for (unsigned int processor_index = 0;
109       processor_index < amount_of_processors;
110       ++processor_index) {
111    // Convert from an array with processor numbers to a bitmask
112    // Processor numbers start at zero.
113    // TODO(hellner): this looks like a bug. Shouldn't the '=' be a '+='?
114    // Or even better |=
115    processor_bit_mask = 1 << processor_numbers[processor_index];
116  }
117  return SetThreadAffinityMask(thread_, processor_bit_mask) != 0;
118}
119
120void ThreadWindows::SetNotAlive() {
121  alive_ = false;
122}
123
124bool ThreadWindows::Stop() {
125  critsect_stop_->Enter();
126
127  // Prevents the handle from being closed in ThreadWindows::Run()
128  do_not_close_handle_ = true;
129  alive_ = false;
130  bool signaled = false;
131  if (thread_ && !dead_) {
132    critsect_stop_->Leave();
133
134    // Wait up to 2 seconds for the thread to complete.
135    if (WAIT_OBJECT_0 == WaitForSingleObject(thread_, 2000)) {
136      signaled = true;
137    }
138    critsect_stop_->Enter();
139  }
140  if (thread_) {
141    CloseHandle(thread_);
142    thread_ = NULL;
143  }
144  critsect_stop_->Leave();
145
146  if (dead_ || signaled) {
147    return true;
148  } else {
149    return false;
150  }
151}
152
153void ThreadWindows::Run() {
154  alive_ = true;
155  dead_ = false;
156  event_->Set();
157
158  // All tracing must be after event_->Set to avoid deadlock in Trace.
159  if (set_thread_name_) {
160    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
161                 "Thread with name:%s started ", name_);
162    SetThreadName(static_cast<DWORD>(-1), name_); // -1 == caller thread.
163  } else {
164    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
165                 "Thread without name started");
166  }
167
168  do {
169    if (run_function_) {
170      if (!run_function_(obj_)) {
171        alive_ = false;
172      }
173    } else {
174      alive_ = false;
175    }
176  } while (alive_);
177
178  if (set_thread_name_) {
179    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
180                 "Thread with name:%s stopped", name_);
181  } else {
182    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
183                 "Thread without name stopped");
184  }
185
186  critsect_stop_->Enter();
187
188  if (thread_ && !do_not_close_handle_) {
189    HANDLE thread = thread_;
190    thread_ = NULL;
191    CloseHandle(thread);
192  }
193  dead_ = true;
194
195  critsect_stop_->Leave();
196};
197
198}  // namespace webrtc
199