Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019 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 | #include "test/time_controller/simulated_time_controller.h" |
| 11 | |
| 12 | #include <algorithm> |
| 13 | #include <deque> |
| 14 | #include <list> |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 15 | #include <memory> |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 16 | #include <string> |
| 17 | #include <thread> |
| 18 | #include <vector> |
| 19 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 20 | #include "absl/strings/string_view.h" |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 21 | #include "test/time_controller/simulated_process_thread.h" |
| 22 | #include "test/time_controller/simulated_task_queue.h" |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 23 | #include "test/time_controller/simulated_thread.h" |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 24 | |
| 25 | namespace webrtc { |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 26 | namespace { |
| 27 | // Helper function to remove from a std container by value. |
| 28 | template <class C> |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 29 | bool RemoveByValue(C* vec, typename C::value_type val) { |
| 30 | auto it = std::find(vec->begin(), vec->end(), val); |
| 31 | if (it == vec->end()) |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 32 | return false; |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 33 | vec->erase(it); |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 34 | return true; |
| 35 | } |
| 36 | } // namespace |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 37 | |
| 38 | namespace sim_time_impl { |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 39 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 40 | SimulatedTimeControllerImpl::SimulatedTimeControllerImpl(Timestamp start_time) |
| 41 | : thread_id_(rtc::CurrentThreadId()), current_time_(start_time) {} |
| 42 | |
| 43 | SimulatedTimeControllerImpl::~SimulatedTimeControllerImpl() = default; |
| 44 | |
| 45 | std::unique_ptr<TaskQueueBase, TaskQueueDeleter> |
| 46 | SimulatedTimeControllerImpl::CreateTaskQueue( |
| 47 | absl::string_view name, |
| 48 | TaskQueueFactory::Priority priority) const { |
| 49 | // TODO(srte): Remove the const cast when the interface is made mutable. |
| 50 | auto mutable_this = const_cast<SimulatedTimeControllerImpl*>(this); |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 51 | auto task_queue = std::unique_ptr<SimulatedTaskQueue, TaskQueueDeleter>( |
| 52 | new SimulatedTaskQueue(mutable_this, name)); |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 53 | ; |
| 54 | mutable_this->Register(task_queue.get()); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 55 | return task_queue; |
| 56 | } |
| 57 | |
| 58 | std::unique_ptr<ProcessThread> SimulatedTimeControllerImpl::CreateProcessThread( |
| 59 | const char* thread_name) { |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 60 | auto process_thread = |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 61 | std::make_unique<SimulatedProcessThread>(this, thread_name); |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 62 | Register(process_thread.get()); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 63 | return process_thread; |
| 64 | } |
| 65 | |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 66 | std::unique_ptr<rtc::Thread> SimulatedTimeControllerImpl::CreateThread( |
| 67 | const std::string& name, |
| 68 | std::unique_ptr<rtc::SocketServer> socket_server) { |
| 69 | auto thread = |
| 70 | std::make_unique<SimulatedThread>(this, name, std::move(socket_server)); |
| 71 | Register(thread.get()); |
| 72 | return thread; |
| 73 | } |
| 74 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 75 | void SimulatedTimeControllerImpl::YieldExecution() { |
| 76 | if (rtc::CurrentThreadId() == thread_id_) { |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 77 | TaskQueueBase* yielding_from = TaskQueueBase::Current(); |
| 78 | // Since we might continue execution on a process thread, we should reset |
| 79 | // the thread local task queue reference. This ensures that thread checkers |
| 80 | // won't think we are executing on the yielding task queue. It also ensure |
| 81 | // that TaskQueueBase::Current() won't return the yielding task queue. |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 82 | TokenTaskQueue::CurrentTaskQueueSetter reset_queue(nullptr); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 83 | // When we yield, we don't want to risk executing further tasks on the |
| 84 | // currently executing task queue. If there's a ready task that also yields, |
| 85 | // it's added to this set as well and only tasks on the remaining task |
| 86 | // queues are executed. |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 87 | auto inserted = yielded_.insert(yielding_from); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 88 | RTC_DCHECK(inserted.second); |
| 89 | RunReadyRunners(); |
| 90 | yielded_.erase(inserted.first); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | void SimulatedTimeControllerImpl::RunReadyRunners() { |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 95 | // Using a dummy thread rather than nullptr to avoid implicit thread creation |
| 96 | // by Thread::Current(). |
| 97 | SimulatedThread::CurrentThreadSetter set_current(dummy_thread_.get()); |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 98 | MutexLock lock(&lock_); |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 99 | RTC_DCHECK_EQ(rtc::CurrentThreadId(), thread_id_); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 100 | Timestamp current_time = CurrentTime(); |
Sebastian Jansson | 1b40823 | 2019-04-12 15:39:38 +0200 | [diff] [blame] | 101 | // Clearing |ready_runners_| in case this is a recursive call: |
| 102 | // RunReadyRunners -> Run -> Event::Wait -> Yield ->RunReadyRunners |
| 103 | ready_runners_.clear(); |
| 104 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 105 | // We repeat until we have no ready left to handle tasks posted by ready |
| 106 | // runners. |
| 107 | while (true) { |
Sebastian Jansson | 7654081 | 2019-04-11 17:48:30 +0200 | [diff] [blame] | 108 | for (auto* runner : runners_) { |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 109 | if (yielded_.find(runner->GetAsTaskQueue()) == yielded_.end() && |
Sebastian Jansson | 7654081 | 2019-04-11 17:48:30 +0200 | [diff] [blame] | 110 | runner->GetNextRunTime() <= current_time) { |
| 111 | ready_runners_.push_back(runner); |
| 112 | } |
| 113 | } |
| 114 | if (ready_runners_.empty()) |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 115 | break; |
Sebastian Jansson | 7654081 | 2019-04-11 17:48:30 +0200 | [diff] [blame] | 116 | while (!ready_runners_.empty()) { |
| 117 | auto* runner = ready_runners_.front(); |
| 118 | ready_runners_.pop_front(); |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 119 | lock_.Unlock(); |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 120 | // Note that the RunReady function might indirectly cause a call to |
Markus Handell | 222598d | 2020-05-15 18:43:05 +0200 | [diff] [blame] | 121 | // Unregister() which will grab |lock_| again to remove items from |
| 122 | // |ready_runners_|. |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 123 | runner->RunReady(current_time); |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 124 | lock_.Lock(); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | Timestamp SimulatedTimeControllerImpl::CurrentTime() const { |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 130 | MutexLock lock(&time_lock_); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 131 | return current_time_; |
| 132 | } |
| 133 | |
| 134 | Timestamp SimulatedTimeControllerImpl::NextRunTime() const { |
| 135 | Timestamp current_time = CurrentTime(); |
| 136 | Timestamp next_time = Timestamp::PlusInfinity(); |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 137 | MutexLock lock(&lock_); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 138 | for (auto* runner : runners_) { |
| 139 | Timestamp next_run_time = runner->GetNextRunTime(); |
| 140 | if (next_run_time <= current_time) |
| 141 | return current_time; |
| 142 | next_time = std::min(next_time, next_run_time); |
| 143 | } |
| 144 | return next_time; |
| 145 | } |
| 146 | |
| 147 | void SimulatedTimeControllerImpl::AdvanceTime(Timestamp target_time) { |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 148 | MutexLock time_lock(&time_lock_); |
Sebastian Jansson | 5a00016 | 2019-04-12 11:21:32 +0200 | [diff] [blame] | 149 | RTC_DCHECK_GE(target_time, current_time_); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 150 | current_time_ = target_time; |
| 151 | } |
| 152 | |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 153 | void SimulatedTimeControllerImpl::Register(SimulatedSequenceRunner* runner) { |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 154 | MutexLock lock(&lock_); |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 155 | runners_.push_back(runner); |
| 156 | } |
| 157 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 158 | void SimulatedTimeControllerImpl::Unregister(SimulatedSequenceRunner* runner) { |
Markus Handell | e56976d | 2020-07-08 17:34:37 +0200 | [diff] [blame] | 159 | MutexLock lock(&lock_); |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 160 | bool removed = RemoveByValue(&runners_, runner); |
Sebastian Jansson | 7b6add3 | 2019-03-29 10:34:26 +0100 | [diff] [blame] | 161 | RTC_CHECK(removed); |
Sebastian Jansson | 53cd9e2 | 2020-01-13 10:33:19 +0100 | [diff] [blame] | 162 | RemoveByValue(&ready_runners_, runner); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 163 | } |
Sebastian Jansson | 274cc7f | 2020-01-17 13:58:54 +0100 | [diff] [blame] | 164 | |
| 165 | void SimulatedTimeControllerImpl::StartYield(TaskQueueBase* yielding_from) { |
| 166 | auto inserted = yielded_.insert(yielding_from); |
| 167 | RTC_DCHECK(inserted.second); |
| 168 | } |
| 169 | |
| 170 | void SimulatedTimeControllerImpl::StopYield(TaskQueueBase* yielding_from) { |
| 171 | yielded_.erase(yielding_from); |
| 172 | } |
Markus Handell | 222598d | 2020-05-15 18:43:05 +0200 | [diff] [blame] | 173 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 174 | } // namespace sim_time_impl |
| 175 | |
| 176 | GlobalSimulatedTimeController::GlobalSimulatedTimeController( |
| 177 | Timestamp start_time) |
Sebastian Jansson | 340af97 | 2019-12-04 10:07:48 +0100 | [diff] [blame] | 178 | : sim_clock_(start_time.us()), impl_(start_time), yield_policy_(&impl_) { |
Sebastian Jansson | d624c39 | 2019-04-17 10:36:03 +0200 | [diff] [blame] | 179 | global_clock_.SetTime(start_time); |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 180 | auto main_thread = std::make_unique<SimulatedMainThread>(&impl_); |
| 181 | impl_.Register(main_thread.get()); |
| 182 | main_thread_ = std::move(main_thread); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | GlobalSimulatedTimeController::~GlobalSimulatedTimeController() = default; |
| 186 | |
| 187 | Clock* GlobalSimulatedTimeController::GetClock() { |
| 188 | return &sim_clock_; |
| 189 | } |
| 190 | |
| 191 | TaskQueueFactory* GlobalSimulatedTimeController::GetTaskQueueFactory() { |
| 192 | return &impl_; |
| 193 | } |
| 194 | |
| 195 | std::unique_ptr<ProcessThread> |
| 196 | GlobalSimulatedTimeController::CreateProcessThread(const char* thread_name) { |
| 197 | return impl_.CreateProcessThread(thread_name); |
| 198 | } |
| 199 | |
Sebastian Jansson | fc8279d | 2020-01-16 11:45:59 +0100 | [diff] [blame] | 200 | std::unique_ptr<rtc::Thread> GlobalSimulatedTimeController::CreateThread( |
| 201 | const std::string& name, |
| 202 | std::unique_ptr<rtc::SocketServer> socket_server) { |
| 203 | return impl_.CreateThread(name, std::move(socket_server)); |
| 204 | } |
| 205 | |
| 206 | rtc::Thread* GlobalSimulatedTimeController::GetMainThread() { |
| 207 | return main_thread_.get(); |
| 208 | } |
| 209 | |
Markus Handell | 486cc55 | 2019-12-03 14:37:28 +0100 | [diff] [blame] | 210 | void GlobalSimulatedTimeController::AdvanceTime(TimeDelta duration) { |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 211 | rtc::ScopedYieldPolicy yield_policy(&impl_); |
| 212 | Timestamp current_time = impl_.CurrentTime(); |
| 213 | Timestamp target_time = current_time + duration; |
| 214 | RTC_DCHECK_EQ(current_time.us(), rtc::TimeMicros()); |
| 215 | while (current_time < target_time) { |
| 216 | impl_.RunReadyRunners(); |
| 217 | Timestamp next_time = std::min(impl_.NextRunTime(), target_time); |
| 218 | impl_.AdvanceTime(next_time); |
| 219 | auto delta = next_time - current_time; |
| 220 | current_time = next_time; |
| 221 | sim_clock_.AdvanceTimeMicroseconds(delta.us()); |
Sebastian Jansson | d624c39 | 2019-04-17 10:36:03 +0200 | [diff] [blame] | 222 | global_clock_.AdvanceTime(delta); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 223 | } |
philipel | d572748 | 2020-01-03 14:43:10 +0100 | [diff] [blame] | 224 | // After time has been simulated up until |target_time| we also need to run |
| 225 | // tasks meant to be executed at |target_time|. |
| 226 | impl_.RunReadyRunners(); |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 227 | } |
| 228 | |
Sebastian Jansson | 0d617cc | 2019-03-22 15:22:16 +0100 | [diff] [blame] | 229 | } // namespace webrtc |