blob: 80db9f422cd207dfbb6f0a968ec87f98670cea52 [file] [log] [blame]
eladalon413ee9a2017-08-22 04:02:52 -07001/*
2 * Copyright (c) 2017 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 "test/single_threaded_task_queue.h"
eladalon413ee9a2017-08-22 04:02:52 -070012
13#include <utility>
14
Karl Wiberg918f50c2018-07-05 11:40:33 +020015#include "absl/memory/memory.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/checks.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010017#include "rtc_base/numerics/safe_conversions.h"
Steve Anton10542f22019-01-11 09:11:00 -080018#include "rtc_base/time_utils.h"
eladalon413ee9a2017-08-22 04:02:52 -070019
20namespace webrtc {
21namespace test {
22
23SingleThreadedTaskQueueForTesting::QueuedTask::QueuedTask(
24 SingleThreadedTaskQueueForTesting::TaskId task_id,
25 int64_t earliest_execution_time,
26 SingleThreadedTaskQueueForTesting::Task task)
27 : task_id(task_id),
28 earliest_execution_time(earliest_execution_time),
29 task(task) {}
30
31SingleThreadedTaskQueueForTesting::QueuedTask::~QueuedTask() = default;
32
33SingleThreadedTaskQueueForTesting::SingleThreadedTaskQueueForTesting(
34 const char* name)
Niels Möllerc572ff32018-11-07 08:43:50 +010035 : thread_(Run, this, name), running_(true), next_task_id_(0) {
eladalon413ee9a2017-08-22 04:02:52 -070036 thread_.Start();
37}
38
39SingleThreadedTaskQueueForTesting::~SingleThreadedTaskQueueForTesting() {
Tommi31d1bce2019-08-27 11:34:20 +020040 Stop();
eladalon413ee9a2017-08-22 04:02:52 -070041}
42
43SingleThreadedTaskQueueForTesting::TaskId
44SingleThreadedTaskQueueForTesting::PostTask(Task task) {
45 return PostDelayedTask(task, 0);
46}
47
48SingleThreadedTaskQueueForTesting::TaskId
49SingleThreadedTaskQueueForTesting::PostDelayedTask(Task task,
50 int64_t delay_ms) {
51 int64_t earliest_exec_time = rtc::TimeAfter(delay_ms);
52
53 rtc::CritScope lock(&cs_);
Tommi31d1bce2019-08-27 11:34:20 +020054 if (!running_)
55 return kInvalidTaskId;
eladalon413ee9a2017-08-22 04:02:52 -070056
57 TaskId id = next_task_id_++;
58
59 // Insert after any other tasks with an earlier-or-equal target time.
60 auto it = tasks_.begin();
61 for (; it != tasks_.end(); it++) {
62 if (earliest_exec_time < (*it)->earliest_execution_time) {
63 break;
64 }
65 }
Karl Wiberg918f50c2018-07-05 11:40:33 +020066 tasks_.insert(it,
67 absl::make_unique<QueuedTask>(id, earliest_exec_time, task));
eladalon413ee9a2017-08-22 04:02:52 -070068
69 // This class is optimized for simplicty, not for performance. This will wake
70 // the thread up even if the next task in the queue is only scheduled for
71 // quite some time from now. In that case, the thread will just send itself
72 // back to sleep.
73 wake_up_.Set();
74
75 return id;
76}
77
78void SingleThreadedTaskQueueForTesting::SendTask(Task task) {
Tommi6e4791f2019-08-14 23:05:44 +020079 RTC_DCHECK(!IsCurrent());
Niels Möllerc572ff32018-11-07 08:43:50 +010080 rtc::Event done;
Tommi31d1bce2019-08-27 11:34:20 +020081 if (PostTask([&task, &done]() {
82 task();
83 done.Set();
84 }) == kInvalidTaskId) {
85 return;
86 }
Tommi6e4791f2019-08-14 23:05:44 +020087 // Give up after 30 seconds, warn after 10.
88 RTC_CHECK(done.Wait(30000, 10000));
eladalon413ee9a2017-08-22 04:02:52 -070089}
90
91bool SingleThreadedTaskQueueForTesting::CancelTask(TaskId task_id) {
92 rtc::CritScope lock(&cs_);
93 for (auto it = tasks_.begin(); it != tasks_.end(); it++) {
94 if ((*it)->task_id == task_id) {
95 tasks_.erase(it);
96 return true;
97 }
98 }
99 return false;
100}
101
Tommi6e4791f2019-08-14 23:05:44 +0200102bool SingleThreadedTaskQueueForTesting::IsCurrent() {
103 return rtc::IsThreadRefEqual(thread_.GetThreadRef(), rtc::CurrentThreadRef());
104}
105
Tommi31d1bce2019-08-27 11:34:20 +0200106bool SingleThreadedTaskQueueForTesting::IsRunning() {
107 RTC_DCHECK_RUN_ON(&owner_thread_checker_);
108 // We could check the |running_| flag here, but this is equivalent for the
109 // purposes of this function.
110 return thread_.IsRunning();
111}
112
113bool SingleThreadedTaskQueueForTesting::HasPendingTasks() const {
114 rtc::CritScope lock(&cs_);
115 return !tasks_.empty();
116}
117
118void SingleThreadedTaskQueueForTesting::Stop() {
119 RTC_DCHECK_RUN_ON(&owner_thread_checker_);
120 if (!thread_.IsRunning())
121 return;
122
123 {
124 rtc::CritScope lock(&cs_);
125 running_ = false;
126 }
127
128 wake_up_.Set();
129 thread_.Stop();
130}
131
eladalon413ee9a2017-08-22 04:02:52 -0700132void SingleThreadedTaskQueueForTesting::Run(void* obj) {
133 static_cast<SingleThreadedTaskQueueForTesting*>(obj)->RunLoop();
134}
135
136void SingleThreadedTaskQueueForTesting::RunLoop() {
137 while (true) {
138 std::unique_ptr<QueuedTask> queued_task;
139
140 // An empty queue would lead to sleeping until the queue becoems non-empty.
Mirko Bonadeidca82bc2017-12-13 18:44:59 +0100141 // A queue where the earliest task is scheduled for later than now, will
eladalon413ee9a2017-08-22 04:02:52 -0700142 // lead to sleeping until the time of the next scheduled task (or until
143 // more tasks are scheduled).
144 int wait_time = rtc::Event::kForever;
145
146 {
147 rtc::CritScope lock(&cs_);
148 if (!running_) {
149 return;
150 }
151 if (!tasks_.empty()) {
152 int64_t remaining_delay_ms = rtc::TimeDiff(
153 tasks_.front()->earliest_execution_time, rtc::TimeMillis());
154 if (remaining_delay_ms <= 0) {
155 queued_task = std::move(tasks_.front());
156 tasks_.pop_front();
157 } else {
158 wait_time = rtc::saturated_cast<int>(remaining_delay_ms);
159 }
160 }
161 }
162
163 if (queued_task) {
164 queued_task->task();
165 } else {
166 wake_up_.Wait(wait_time);
167 }
168 }
169}
170
171} // namespace test
172} // namespace webrtc