blob: ca2ce1367b336abd687ddf5a98e339e7f0adfd5a [file] [log] [blame]
Tommibebc6902015-05-18 09:51:42 +02001/*
2 * Copyright (c) 2015 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "rtc_base/platform_thread.h"
Tommibebc6902015-05-18 09:51:42 +020012
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/atomicops.h"
14#include "rtc_base/checks.h"
15#include "rtc_base/timeutils.h"
16#include "rtc_base/trace_event.h"
Tommibebc6902015-05-18 09:51:42 +020017
18#if defined(WEBRTC_LINUX)
Tommiea14f0a2015-05-18 13:51:06 +020019#include <sys/prctl.h>
Tommibebc6902015-05-18 09:51:42 +020020#include <sys/syscall.h>
21#endif
22
23namespace rtc {
pbos12411ef2015-11-23 14:47:56 -080024namespace {
25#if defined(WEBRTC_WIN)
26void CALLBACK RaiseFlag(ULONG_PTR param) {
27 *reinterpret_cast<bool*>(param) = true;
28}
29#else
30struct ThreadAttributes {
31 ThreadAttributes() { pthread_attr_init(&attr); }
32 ~ThreadAttributes() { pthread_attr_destroy(&attr); }
33 pthread_attr_t* operator&() { return &attr; }
34 pthread_attr_t attr;
35};
pbos12411ef2015-11-23 14:47:56 -080036#endif // defined(WEBRTC_WIN)
37}
38
tommi0f8b4032017-02-22 11:22:05 -080039PlatformThread::PlatformThread(ThreadRunFunctionDeprecated func,
pbos12411ef2015-11-23 14:47:56 -080040 void* obj,
41 const char* thread_name)
tommi0f8b4032017-02-22 11:22:05 -080042 : run_function_deprecated_(func),
pbos12411ef2015-11-23 14:47:56 -080043 obj_(obj),
tommi82ead602017-02-19 16:09:55 -080044 name_(thread_name ? thread_name : "webrtc") {
pbos12411ef2015-11-23 14:47:56 -080045 RTC_DCHECK(func);
46 RTC_DCHECK(name_.length() < 64);
tommi0f8b4032017-02-22 11:22:05 -080047 spawned_thread_checker_.DetachFromThread();
48}
49
50PlatformThread::PlatformThread(ThreadRunFunction func,
51 void* obj,
52 const char* thread_name,
53 ThreadPriority priority /*= kNormalPriority*/)
54 : run_function_(func), priority_(priority), obj_(obj), name_(thread_name) {
55 RTC_DCHECK(func);
56 RTC_DCHECK(!name_.empty());
57 // TODO(tommi): Consider lowering the limit to 15 (limit on Linux).
58 RTC_DCHECK(name_.length() < 64);
59 spawned_thread_checker_.DetachFromThread();
pbos12411ef2015-11-23 14:47:56 -080060}
61
62PlatformThread::~PlatformThread() {
63 RTC_DCHECK(thread_checker_.CalledOnValidThread());
64#if defined(WEBRTC_WIN)
65 RTC_DCHECK(!thread_);
tommi845afa82016-04-22 09:08:44 -070066 RTC_DCHECK(!thread_id_);
pbos12411ef2015-11-23 14:47:56 -080067#endif // defined(WEBRTC_WIN)
68}
69
70#if defined(WEBRTC_WIN)
71DWORD WINAPI PlatformThread::StartThread(void* param) {
perkj6a2e20a2016-11-30 04:53:08 -080072 // The GetLastError() function only returns valid results when it is called
73 // after a Win32 API function that returns a "failed" result. A crash dump
74 // contains the result from GetLastError() and to make sure it does not
75 // falsely report a Windows error we call SetLastError here.
76 ::SetLastError(ERROR_SUCCESS);
pbos12411ef2015-11-23 14:47:56 -080077 static_cast<PlatformThread*>(param)->Run();
78 return 0;
79}
80#else
81void* PlatformThread::StartThread(void* param) {
82 static_cast<PlatformThread*>(param)->Run();
83 return 0;
84}
85#endif // defined(WEBRTC_WIN)
86
Peter Boström8c38e8b2015-11-26 17:45:47 +010087void PlatformThread::Start() {
pbos12411ef2015-11-23 14:47:56 -080088 RTC_DCHECK(thread_checker_.CalledOnValidThread());
89 RTC_DCHECK(!thread_) << "Thread already started?";
90#if defined(WEBRTC_WIN)
91 stop_ = false;
92
93 // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
94 // Set the reserved stack stack size to 1M, which is the default on Windows
95 // and Linux.
deadbeef37f5ecf2017-02-27 14:06:41 -080096 thread_ = ::CreateThread(nullptr, 1024 * 1024, &StartThread, this,
tommi845afa82016-04-22 09:08:44 -070097 STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
pbos12411ef2015-11-23 14:47:56 -080098 RTC_CHECK(thread_) << "CreateThread failed";
tommi845afa82016-04-22 09:08:44 -070099 RTC_DCHECK(thread_id_);
pbos12411ef2015-11-23 14:47:56 -0800100#else
101 ThreadAttributes attr;
102 // Set the stack stack size to 1M.
103 pthread_attr_setstacksize(&attr, 1024 * 1024);
104 RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
105#endif // defined(WEBRTC_WIN)
pbos12411ef2015-11-23 14:47:56 -0800106}
107
Peter Boström8c38e8b2015-11-26 17:45:47 +0100108bool PlatformThread::IsRunning() const {
pbos12411ef2015-11-23 14:47:56 -0800109 RTC_DCHECK(thread_checker_.CalledOnValidThread());
110#if defined(WEBRTC_WIN)
Peter Boström8c38e8b2015-11-26 17:45:47 +0100111 return thread_ != nullptr;
pbos12411ef2015-11-23 14:47:56 -0800112#else
Peter Boström8c38e8b2015-11-26 17:45:47 +0100113 return thread_ != 0;
114#endif // defined(WEBRTC_WIN)
115}
pbos12411ef2015-11-23 14:47:56 -0800116
tommi845afa82016-04-22 09:08:44 -0700117PlatformThreadRef PlatformThread::GetThreadRef() const {
118#if defined(WEBRTC_WIN)
119 return thread_id_;
120#else
121 return thread_;
122#endif // defined(WEBRTC_WIN)
123}
124
Peter Boström8c38e8b2015-11-26 17:45:47 +0100125void PlatformThread::Stop() {
126 RTC_DCHECK(thread_checker_.CalledOnValidThread());
127 if (!IsRunning())
128 return;
129
130#if defined(WEBRTC_WIN)
131 // Set stop_ to |true| on the worker thread.
tommi845afa82016-04-22 09:08:44 -0700132 bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_));
133 // Queuing the APC can fail if the thread is being terminated.
134 RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE);
Peter Boström8c38e8b2015-11-26 17:45:47 +0100135 WaitForSingleObject(thread_, INFINITE);
136 CloseHandle(thread_);
137 thread_ = nullptr;
tommi845afa82016-04-22 09:08:44 -0700138 thread_id_ = 0;
Peter Boström8c38e8b2015-11-26 17:45:47 +0100139#else
tommi0f8b4032017-02-22 11:22:05 -0800140 if (!run_function_)
141 RTC_CHECK_EQ(1, AtomicOps::Increment(&stop_flag_));
pbos12411ef2015-11-23 14:47:56 -0800142 RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
tommi0f8b4032017-02-22 11:22:05 -0800143 if (!run_function_)
144 AtomicOps::ReleaseStore(&stop_flag_, 0);
pbos12411ef2015-11-23 14:47:56 -0800145 thread_ = 0;
146#endif // defined(WEBRTC_WIN)
tommi0f8b4032017-02-22 11:22:05 -0800147 spawned_thread_checker_.DetachFromThread();
pbos12411ef2015-11-23 14:47:56 -0800148}
149
tommi82ead602017-02-19 16:09:55 -0800150// TODO(tommi): Deprecate the loop behavior in PlatformThread.
151// * Introduce a new callback type that returns void.
152// * Remove potential for a busy loop in PlatformThread.
153// * Delegate the responsibility for how to stop the thread, to the
154// implementation that actually uses the thread.
155// All implementations will need to be aware of how the thread should be stopped
156// and encouraging a busy polling loop, can be costly in terms of power and cpu.
pbos12411ef2015-11-23 14:47:56 -0800157void PlatformThread::Run() {
tommi0f8b4032017-02-22 11:22:05 -0800158 // Attach the worker thread checker to this thread.
159 RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
160 rtc::SetCurrentThreadName(name_.c_str());
161
162 if (run_function_) {
163 SetPriority(priority_);
164 run_function_(obj_);
165 return;
166 }
tommi500f1b72017-03-02 07:07:09 -0800167
168// TODO(tommi): Delete the rest of this function when looping isn't supported.
169#if RTC_DCHECK_IS_ON
170 // These constants control the busy loop detection algorithm below.
171 // |kMaxLoopCount| controls the limit for how many times we allow the loop
172 // to run within a period, before DCHECKing.
173 // |kPeriodToMeasureMs| controls how long that period is.
174 static const int kMaxLoopCount = 1000;
175 static const int kPeriodToMeasureMs = 100;
176 int64_t loop_stamps[kMaxLoopCount] = {};
177 int64_t sequence_nr = 0;
danilchap27523472017-02-28 06:20:38 -0800178#endif
tommi500f1b72017-03-02 07:07:09 -0800179
pbos12411ef2015-11-23 14:47:56 -0800180 do {
tommidb23ea62017-03-03 07:21:18 -0800181 TRACE_EVENT1("webrtc", "PlatformThread::Run", "name", name_.c_str());
182
sprange791ffd2016-01-26 01:53:20 -0800183 // The interface contract of Start/Stop is that for a successful call to
pbos12411ef2015-11-23 14:47:56 -0800184 // Start, there should be at least one call to the run function. So we
185 // call the function before checking |stop_|.
tommi0f8b4032017-02-22 11:22:05 -0800186 if (!run_function_deprecated_(obj_))
pbos12411ef2015-11-23 14:47:56 -0800187 break;
tommi500f1b72017-03-02 07:07:09 -0800188#if RTC_DCHECK_IS_ON
189 auto id = sequence_nr % kMaxLoopCount;
190 loop_stamps[id] = rtc::TimeMillis();
191 if (sequence_nr > kMaxLoopCount) {
192 auto compare_id = (id + 1) % kMaxLoopCount;
193 auto diff = loop_stamps[id] - loop_stamps[compare_id];
194 RTC_DCHECK_GE(diff, 0);
195 if (diff < kPeriodToMeasureMs) {
196 RTC_NOTREACHED() << "This thread is too busy: " << name_ << " " << diff
197 << "ms sequence=" << sequence_nr << " "
198 << loop_stamps[id] << " vs " << loop_stamps[compare_id]
199 << ", " << id << " vs " << compare_id;
200 }
201 }
202 ++sequence_nr;
203#endif
pbos12411ef2015-11-23 14:47:56 -0800204#if defined(WEBRTC_WIN)
205 // Alertable sleep to permit RaiseFlag to run and update |stop_|.
206 SleepEx(0, true);
207 } while (!stop_);
208#else
Yura Yaroshevich665d18e2018-01-23 17:10:29 +0300209#if defined(WEBRTC_MAC) || defined(WEBRTC_ANDROID)
tommi0473b1d2017-03-02 08:08:59 -0800210 sched_yield();
211#else
tommi500f1b72017-03-02 07:07:09 -0800212 static const struct timespec ts_null = {0};
danilchap27523472017-02-28 06:20:38 -0800213 nanosleep(&ts_null, nullptr);
214#endif
tommi82ead602017-02-19 16:09:55 -0800215 } while (!AtomicOps::AcquireLoad(&stop_flag_));
pbos12411ef2015-11-23 14:47:56 -0800216#endif // defined(WEBRTC_WIN)
217}
218
219bool PlatformThread::SetPriority(ThreadPriority priority) {
tommi0f8b4032017-02-22 11:22:05 -0800220#if RTC_DCHECK_IS_ON
221 if (run_function_) {
222 // The non-deprecated way of how this function gets called, is that it must
223 // be called on the worker thread itself.
tommi7c3da272017-03-20 03:47:17 -0700224 RTC_DCHECK(!thread_checker_.CalledOnValidThread());
tommi0f8b4032017-02-22 11:22:05 -0800225 RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
226 } else {
227 // In the case of deprecated use of this method, it must be called on the
228 // same thread as the PlatformThread object is constructed on.
229 RTC_DCHECK(thread_checker_.CalledOnValidThread());
230 RTC_DCHECK(IsRunning());
231 }
232#endif
233
Peter Boströmc6612132015-11-24 18:10:24 +0100234#if defined(WEBRTC_WIN)
Peter Boström8c38e8b2015-11-26 17:45:47 +0100235 return SetThreadPriority(thread_, priority) != FALSE;
Wez0614ed92018-02-06 13:38:21 -0800236#elif defined(__native_client__) || defined(WEBRTC_FUCHSIA)
237 // Setting thread priorities is not supported in NaCl or Fuchsia.
Peter Boströmc6612132015-11-24 18:10:24 +0100238 return true;
239#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
240 // TODO(tommi): Switch to the same mechanism as Chromium uses for changing
241 // thread priorities.
pbos12411ef2015-11-23 14:47:56 -0800242 return true;
243#else
244#ifdef WEBRTC_THREAD_RR
245 const int policy = SCHED_RR;
246#else
247 const int policy = SCHED_FIFO;
248#endif
249 const int min_prio = sched_get_priority_min(policy);
250 const int max_prio = sched_get_priority_max(policy);
251 if (min_prio == -1 || max_prio == -1) {
252 return false;
253 }
254
255 if (max_prio - min_prio <= 2)
256 return false;
257
Peter Boström97c821d2015-11-24 13:48:13 +0100258 // Convert webrtc priority to system priorities:
pbos12411ef2015-11-23 14:47:56 -0800259 sched_param param;
Peter Boström97c821d2015-11-24 13:48:13 +0100260 const int top_prio = max_prio - 1;
261 const int low_prio = min_prio + 1;
262 switch (priority) {
263 case kLowPriority:
264 param.sched_priority = low_prio;
265 break;
266 case kNormalPriority:
267 // The -1 ensures that the kHighPriority is always greater or equal to
268 // kNormalPriority.
269 param.sched_priority = (low_prio + top_prio - 1) / 2;
270 break;
271 case kHighPriority:
272 param.sched_priority = std::max(top_prio - 2, low_prio);
273 break;
274 case kHighestPriority:
275 param.sched_priority = std::max(top_prio - 1, low_prio);
276 break;
277 case kRealtimePriority:
278 param.sched_priority = top_prio;
279 break;
pbos12411ef2015-11-23 14:47:56 -0800280 }
Peter Boström97c821d2015-11-24 13:48:13 +0100281 return pthread_setschedparam(thread_, policy, &param) == 0;
pbos12411ef2015-11-23 14:47:56 -0800282#endif // defined(WEBRTC_WIN)
283}
284
tommi845afa82016-04-22 09:08:44 -0700285#if defined(WEBRTC_WIN)
286bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) {
287 RTC_DCHECK(thread_checker_.CalledOnValidThread());
288 RTC_DCHECK(IsRunning());
289
290 return QueueUserAPC(function, thread_, data) != FALSE;
291}
292#endif
293
Peter Boström8c38e8b2015-11-26 17:45:47 +0100294} // namespace rtc