| /* | |
| * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include "test/single_threaded_task_queue.h" | |
| #include <utility> | |
| #include "absl/memory/memory.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/numerics/safe_conversions.h" | |
| #include "rtc_base/timeutils.h" | |
| namespace webrtc { | |
| namespace test { | |
| SingleThreadedTaskQueueForTesting::QueuedTask::QueuedTask( | |
| SingleThreadedTaskQueueForTesting::TaskId task_id, | |
| int64_t earliest_execution_time, | |
| SingleThreadedTaskQueueForTesting::Task task) | |
| : task_id(task_id), | |
| earliest_execution_time(earliest_execution_time), | |
| task(task) {} | |
| SingleThreadedTaskQueueForTesting::QueuedTask::~QueuedTask() = default; | |
| SingleThreadedTaskQueueForTesting::SingleThreadedTaskQueueForTesting( | |
| const char* name) | |
| : thread_(Run, this, name), running_(true), next_task_id_(0) { | |
| thread_.Start(); | |
| } | |
| SingleThreadedTaskQueueForTesting::~SingleThreadedTaskQueueForTesting() { | |
| RTC_DCHECK_RUN_ON(&owner_thread_checker_); | |
| { | |
| rtc::CritScope lock(&cs_); | |
| running_ = false; | |
| } | |
| wake_up_.Set(); | |
| thread_.Stop(); | |
| } | |
| SingleThreadedTaskQueueForTesting::TaskId | |
| SingleThreadedTaskQueueForTesting::PostTask(Task task) { | |
| return PostDelayedTask(task, 0); | |
| } | |
| SingleThreadedTaskQueueForTesting::TaskId | |
| SingleThreadedTaskQueueForTesting::PostDelayedTask(Task task, | |
| int64_t delay_ms) { | |
| int64_t earliest_exec_time = rtc::TimeAfter(delay_ms); | |
| rtc::CritScope lock(&cs_); | |
| TaskId id = next_task_id_++; | |
| // Insert after any other tasks with an earlier-or-equal target time. | |
| auto it = tasks_.begin(); | |
| for (; it != tasks_.end(); it++) { | |
| if (earliest_exec_time < (*it)->earliest_execution_time) { | |
| break; | |
| } | |
| } | |
| tasks_.insert(it, | |
| absl::make_unique<QueuedTask>(id, earliest_exec_time, task)); | |
| // This class is optimized for simplicty, not for performance. This will wake | |
| // the thread up even if the next task in the queue is only scheduled for | |
| // quite some time from now. In that case, the thread will just send itself | |
| // back to sleep. | |
| wake_up_.Set(); | |
| return id; | |
| } | |
| void SingleThreadedTaskQueueForTesting::SendTask(Task task) { | |
| rtc::Event done; | |
| PostTask([&task, &done]() { | |
| task(); | |
| done.Set(); | |
| }); | |
| done.Wait(rtc::Event::kForever); | |
| } | |
| bool SingleThreadedTaskQueueForTesting::CancelTask(TaskId task_id) { | |
| rtc::CritScope lock(&cs_); | |
| for (auto it = tasks_.begin(); it != tasks_.end(); it++) { | |
| if ((*it)->task_id == task_id) { | |
| tasks_.erase(it); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void SingleThreadedTaskQueueForTesting::Run(void* obj) { | |
| static_cast<SingleThreadedTaskQueueForTesting*>(obj)->RunLoop(); | |
| } | |
| void SingleThreadedTaskQueueForTesting::RunLoop() { | |
| while (true) { | |
| std::unique_ptr<QueuedTask> queued_task; | |
| // An empty queue would lead to sleeping until the queue becoems non-empty. | |
| // A queue where the earliest task is scheduled for later than now, will | |
| // lead to sleeping until the time of the next scheduled task (or until | |
| // more tasks are scheduled). | |
| int wait_time = rtc::Event::kForever; | |
| { | |
| rtc::CritScope lock(&cs_); | |
| if (!running_) { | |
| return; | |
| } | |
| if (!tasks_.empty()) { | |
| int64_t remaining_delay_ms = rtc::TimeDiff( | |
| tasks_.front()->earliest_execution_time, rtc::TimeMillis()); | |
| if (remaining_delay_ms <= 0) { | |
| queued_task = std::move(tasks_.front()); | |
| tasks_.pop_front(); | |
| } else { | |
| wait_time = rtc::saturated_cast<int>(remaining_delay_ms); | |
| } | |
| } | |
| } | |
| if (queued_task) { | |
| queued_task->task(); | |
| } else { | |
| wake_up_.Wait(wait_time); | |
| } | |
| } | |
| } | |
| } // namespace test | |
| } // namespace webrtc |