blob: 6252931544b1b7b0ee257b2b7da8b649ed65df39 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
xians@webrtc.org6bde7a82012-02-20 08:39:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
pbos@webrtc.org8b062002013-07-12 08:28:10 +000011#include "webrtc/modules/utility/source/process_thread_impl.h"
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +000012
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000013#include "webrtc/base/checks.h"
tommi435f98b2016-05-28 14:57:15 -070014#include "webrtc/base/task_queue.h"
Niels Möllerd28db7f2016-05-10 16:31:47 +020015#include "webrtc/base/timeutils.h"
tommidea489f2017-03-03 03:20:24 -080016#include "webrtc/base/trace_event.h"
Henrik Kjellanderff761fb2015-11-04 08:31:52 +010017#include "webrtc/modules/include/module.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010018#include "webrtc/system_wrappers/include/logging.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019
20namespace webrtc {
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000021namespace {
tommi@webrtc.org3985f012015-02-27 13:36:34 +000022
23// We use this constant internally to signal that a module has requested
24// a callback right away. When this is set, no call to TimeUntilNextProcess
25// should be made, but Process() should be called directly.
26const int64_t kCallProcessImmediately = -1;
27
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000028int64_t GetNextCallbackTime(Module* module, int64_t time_now) {
29 int64_t interval = module->TimeUntilNextProcess();
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000030 if (interval < 0) {
pbos9e260f12015-08-20 01:23:48 -070031 // Falling behind, we should call the callback now.
32 return time_now;
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000033 }
34 return time_now + interval;
35}
niklase@google.com470e71d2011-07-07 08:21:25 +000036}
37
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000038ProcessThread::~ProcessThread() {}
niklase@google.com470e71d2011-07-07 08:21:25 +000039
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000040// static
kwiberg84be5112016-04-27 01:19:58 -070041std::unique_ptr<ProcessThread> ProcessThread::Create(
stefan847855b2015-09-11 09:52:15 -070042 const char* thread_name) {
kwiberg84be5112016-04-27 01:19:58 -070043 return std::unique_ptr<ProcessThread>(new ProcessThreadImpl(thread_name));
niklase@google.com470e71d2011-07-07 08:21:25 +000044}
45
stefan847855b2015-09-11 09:52:15 -070046ProcessThreadImpl::ProcessThreadImpl(const char* thread_name)
47 : wake_up_(EventWrapper::Create()),
48 stop_(false),
49 thread_name_(thread_name) {}
niklase@google.com470e71d2011-07-07 08:21:25 +000050
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000051ProcessThreadImpl::~ProcessThreadImpl() {
henrikg91d6ede2015-09-17 00:24:34 -070052 RTC_DCHECK(thread_checker_.CalledOnValidThread());
53 RTC_DCHECK(!thread_.get());
54 RTC_DCHECK(!stop_);
tommi@webrtc.org03054482015-03-05 13:13:42 +000055
56 while (!queue_.empty()) {
57 delete queue_.front();
58 queue_.pop();
59 }
niklase@google.com470e71d2011-07-07 08:21:25 +000060}
61
tommi@webrtc.org3985f012015-02-27 13:36:34 +000062void ProcessThreadImpl::Start() {
henrikg91d6ede2015-09-17 00:24:34 -070063 RTC_DCHECK(thread_checker_.CalledOnValidThread());
64 RTC_DCHECK(!thread_.get());
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000065 if (thread_.get())
tommi@webrtc.org3985f012015-02-27 13:36:34 +000066 return;
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000067
henrikg91d6ede2015-09-17 00:24:34 -070068 RTC_DCHECK(!stop_);
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000069
Tommi842a4a62015-03-30 14:16:12 +000070 {
71 // TODO(tommi): Since DeRegisterModule is currently being called from
72 // different threads in some cases (ChannelOwner), we need to lock access to
73 // the modules_ collection even on the controller thread.
74 // Once we've cleaned up those places, we can remove this lock.
75 rtc::CritScope lock(&lock_);
76 for (ModuleCallback& m : modules_)
77 m.module->ProcessThreadAttached(this);
78 }
tommi@webrtc.org3985f012015-02-27 13:36:34 +000079
Peter Boström8c38e8b2015-11-26 17:45:47 +010080 thread_.reset(
81 new rtc::PlatformThread(&ProcessThreadImpl::Run, this, thread_name_));
82 thread_->Start();
niklase@google.com470e71d2011-07-07 08:21:25 +000083}
84
tommi@webrtc.org3985f012015-02-27 13:36:34 +000085void ProcessThreadImpl::Stop() {
henrikg91d6ede2015-09-17 00:24:34 -070086 RTC_DCHECK(thread_checker_.CalledOnValidThread());
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000087 if(!thread_.get())
tommi@webrtc.org3985f012015-02-27 13:36:34 +000088 return;
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000089
90 {
91 rtc::CritScope lock(&lock_);
92 stop_ = true;
93 }
94
95 wake_up_->Set();
96
Peter Boström8c38e8b2015-11-26 17:45:47 +010097 thread_->Stop();
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +000098 stop_ = false;
99
Tommi842a4a62015-03-30 14:16:12 +0000100 // TODO(tommi): Since DeRegisterModule is currently being called from
101 // different threads in some cases (ChannelOwner), we need to lock access to
102 // the modules_ collection even on the controller thread.
Tommi7f375f02015-04-02 14:50:21 +0000103 // Since DeRegisterModule also checks thread_, we also need to hold the
104 // lock for the .reset() operation.
Tommi842a4a62015-03-30 14:16:12 +0000105 // Once we've cleaned up those places, we can remove this lock.
106 rtc::CritScope lock(&lock_);
Tommi7f375f02015-04-02 14:50:21 +0000107 thread_.reset();
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000108 for (ModuleCallback& m : modules_)
109 m.module->ProcessThreadAttached(nullptr);
niklase@google.com470e71d2011-07-07 08:21:25 +0000110}
111
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000112void ProcessThreadImpl::WakeUp(Module* module) {
113 // Allowed to be called on any thread.
114 {
115 rtc::CritScope lock(&lock_);
tommi@webrtc.org103f3282015-02-08 00:48:10 +0000116 for (ModuleCallback& m : modules_) {
117 if (m.module == module)
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000118 m.next_callback = kCallProcessImmediately;
tommi@webrtc.org103f3282015-02-08 00:48:10 +0000119 }
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000120 }
121 wake_up_->Set();
122}
123
tommi435f98b2016-05-28 14:57:15 -0700124void ProcessThreadImpl::PostTask(std::unique_ptr<rtc::QueuedTask> task) {
tommi@webrtc.org03054482015-03-05 13:13:42 +0000125 // Allowed to be called on any thread.
126 {
127 rtc::CritScope lock(&lock_);
128 queue_.push(task.release());
129 }
130 wake_up_->Set();
131}
132
tommidea489f2017-03-03 03:20:24 -0800133void ProcessThreadImpl::RegisterModule(Module* module,
134 const rtc::Location& from) {
henrikg91d6ede2015-09-17 00:24:34 -0700135 RTC_DCHECK(thread_checker_.CalledOnValidThread());
136 RTC_DCHECK(module);
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000137
kwiberg5377bc72016-10-04 13:46:56 -0700138#if RTC_DCHECK_IS_ON
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000139 {
140 // Catch programmer error.
141 rtc::CritScope lock(&lock_);
142 for (const ModuleCallback& mc : modules_)
henrikg91d6ede2015-09-17 00:24:34 -0700143 RTC_DCHECK(mc.module != module);
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000144 }
145#endif
146
147 // Now that we know the module isn't in the list, we'll call out to notify
148 // the module that it's attached to the worker thread. We don't hold
149 // the lock while we make this call.
150 if (thread_.get())
151 module->ProcessThreadAttached(this);
152
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000153 {
154 rtc::CritScope lock(&lock_);
tommidea489f2017-03-03 03:20:24 -0800155 modules_.push_back(ModuleCallback(module, from));
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000156 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000157
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000158 // Wake the thread calling ProcessThreadImpl::Process() to update the
159 // waiting time. The waiting time for the just registered module may be
160 // shorter than all other registered modules.
161 wake_up_->Set();
niklase@google.com470e71d2011-07-07 08:21:25 +0000162}
163
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000164void ProcessThreadImpl::DeRegisterModule(Module* module) {
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000165 // Allowed to be called on any thread.
Tommi7f375f02015-04-02 14:50:21 +0000166 // TODO(tommi): Disallow this ^^^
henrikg91d6ede2015-09-17 00:24:34 -0700167 RTC_DCHECK(module);
Tommi7f375f02015-04-02 14:50:21 +0000168
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000169 {
170 rtc::CritScope lock(&lock_);
171 modules_.remove_if([&module](const ModuleCallback& m) {
172 return m.module == module;
173 });
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000174
Tommi7f375f02015-04-02 14:50:21 +0000175 // TODO(tommi): we currently need to hold the lock while calling out to
176 // ProcessThreadAttached. This is to make sure that the thread hasn't been
177 // destroyed while we attach the module. Once we can make sure
178 // DeRegisterModule isn't being called on arbitrary threads, we can move the
179 // |if (thread_.get())| check and ProcessThreadAttached() call outside the
180 // lock scope.
181
182 // Notify the module that it's been detached.
183 if (thread_.get())
184 module->ProcessThreadAttached(nullptr);
185 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000186}
187
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000188// static
189bool ProcessThreadImpl::Run(void* obj) {
190 return static_cast<ProcessThreadImpl*>(obj)->Process();
niklase@google.com470e71d2011-07-07 08:21:25 +0000191}
192
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000193bool ProcessThreadImpl::Process() {
tommidea489f2017-03-03 03:20:24 -0800194 TRACE_EVENT1("webrtc", "ProcessThreadImpl", "name", thread_name_);
Niels Möllerd28db7f2016-05-10 16:31:47 +0200195 int64_t now = rtc::TimeMillis();
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000196 int64_t next_checkpoint = now + (1000 * 60);
tommi@webrtc.org03054482015-03-05 13:13:42 +0000197
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000198 {
199 rtc::CritScope lock(&lock_);
200 if (stop_)
201 return false;
tommi@webrtc.org103f3282015-02-08 00:48:10 +0000202 for (ModuleCallback& m : modules_) {
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000203 // TODO(tommi): Would be good to measure the time TimeUntilNextProcess
204 // takes and dcheck if it takes too long (e.g. >=10ms). Ideally this
205 // operation should not require taking a lock, so querying all modules
206 // should run in a matter of nanoseconds.
207 if (m.next_callback == 0)
208 m.next_callback = GetNextCallbackTime(m.module, now);
niklase@google.com470e71d2011-07-07 08:21:25 +0000209
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000210 if (m.next_callback <= now ||
211 m.next_callback == kCallProcessImmediately) {
tommidea489f2017-03-03 03:20:24 -0800212 {
213 TRACE_EVENT2("webrtc", "ModuleProcess", "function",
214 m.location.function_name(), "file",
215 m.location.file_and_line());
216 m.module->Process();
217 }
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000218 // Use a new 'now' reference to calculate when the next callback
219 // should occur. We'll continue to use 'now' above for the baseline
220 // of calculating how long we should wait, to reduce variance.
Niels Möllerd28db7f2016-05-10 16:31:47 +0200221 int64_t new_now = rtc::TimeMillis();
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000222 m.next_callback = GetNextCallbackTime(m.module, new_now);
223 }
224
225 if (m.next_callback < next_checkpoint)
226 next_checkpoint = m.next_callback;
niklase@google.com470e71d2011-07-07 08:21:25 +0000227 }
tommi@webrtc.org03054482015-03-05 13:13:42 +0000228
229 while (!queue_.empty()) {
tommi435f98b2016-05-28 14:57:15 -0700230 rtc::QueuedTask* task = queue_.front();
tommi@webrtc.org03054482015-03-05 13:13:42 +0000231 queue_.pop();
232 lock_.Leave();
233 task->Run();
234 delete task;
235 lock_.Enter();
236 }
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000237 }
238
Niels Möllerd28db7f2016-05-10 16:31:47 +0200239 int64_t time_to_wait = next_checkpoint - rtc::TimeMillis();
tommi@webrtc.org0c3e12b2015-02-06 09:44:12 +0000240 if (time_to_wait > 0)
241 wake_up_->Wait(static_cast<unsigned long>(time_to_wait));
242
243 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000244}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000245} // namespace webrtc