Add TimeController to api/test/ and add a CreateTimeController API.
Creates an abstraction for an "alarm clock" which can schedule time-controller callbacks and exposes a time controller driven by an external alarm. Bug: webrtc:9719 Change-Id: I08c2aa9dba25603043bfba48f55c925716a55bae Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158969 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Per Kjellander <perkj@webrtc.org> Reviewed-by: Artem Titov <titovartem@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Commit-Queue: Bjorn Mellem <mellem@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29879}
This commit is contained in:

committed by
Commit Bot

parent
3daedb6c88
commit
c4f865413a
46
api/BUILD.gn
46
api/BUILD.gn
@ -946,6 +946,45 @@ if (rtc_include_tests) {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("time_controller") {
|
||||
visibility = [ "*" ]
|
||||
testonly = true
|
||||
sources = [
|
||||
"test/time_controller.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"../modules:module_api",
|
||||
"../modules/utility:utility",
|
||||
"../rtc_base",
|
||||
"../rtc_base:rtc_base_tests_utils",
|
||||
"../rtc_base:rtc_event",
|
||||
"../rtc_base/synchronization:sequence_checker",
|
||||
"../rtc_base/synchronization:yield_policy",
|
||||
"../rtc_base/task_utils:to_queued_task",
|
||||
"../system_wrappers",
|
||||
"task_queue",
|
||||
"task_queue:default_task_queue_factory",
|
||||
"units:time_delta",
|
||||
"units:timestamp",
|
||||
"//third_party/abseil-cpp/absl/strings",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("create_time_controller") {
|
||||
visibility = [ "*" ]
|
||||
testonly = true
|
||||
sources = [
|
||||
"test/create_time_controller.cc",
|
||||
"test/create_time_controller.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":time_controller",
|
||||
"../test/time_controller",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("rtc_api_unittests") {
|
||||
testonly = true
|
||||
|
||||
@ -958,11 +997,13 @@ if (rtc_include_tests) {
|
||||
"rtp_packet_infos_unittest.cc",
|
||||
"rtp_parameters_unittest.cc",
|
||||
"scoped_refptr_unittest.cc",
|
||||
"test/create_time_controller_unittest.cc",
|
||||
"test/loopback_media_transport_unittest.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":array_view",
|
||||
":create_time_controller",
|
||||
":function_view",
|
||||
":libjingle_peerconnection_api",
|
||||
":loopback_media_transport",
|
||||
@ -971,12 +1012,17 @@ if (rtc_include_tests) {
|
||||
":rtp_packet_info",
|
||||
":rtp_parameters",
|
||||
":scoped_refptr",
|
||||
":time_controller",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:gunit_helpers",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:rtc_task_queue",
|
||||
"../rtc_base/task_utils:repeating_task",
|
||||
"../test:fileutils",
|
||||
"../test:test_support",
|
||||
"task_queue:task_queue_default_factory_unittests",
|
||||
"units:time_delta",
|
||||
"units:timestamp",
|
||||
"units:units_unittests",
|
||||
"video:video_unittests",
|
||||
]
|
||||
|
@ -32,4 +32,9 @@ specific_include_rules = {
|
||||
"+rtc_base/thread.h",
|
||||
"+media/base/media_constants.h",
|
||||
],
|
||||
"time_controller\.h": [
|
||||
"+modules/utility/include/process_thread.h",
|
||||
"+rtc_base/synchronization/yield_policy.h",
|
||||
"+system_wrappers/include/clock.h",
|
||||
],
|
||||
}
|
||||
|
24
api/test/create_time_controller.cc
Normal file
24
api/test/create_time_controller.cc
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2019 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 "api/test/create_time_controller.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "test/time_controller/external_time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::unique_ptr<TimeController> CreateTimeController(
|
||||
ControlledAlarmClock* alarm) {
|
||||
return std::make_unique<ExternalTimeController>(alarm);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
24
api/test/create_time_controller.h
Normal file
24
api/test/create_time_controller.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
#ifndef API_TEST_CREATE_TIME_CONTROLLER_H_
|
||||
#define API_TEST_CREATE_TIME_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/test/time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::unique_ptr<TimeController> CreateTimeController(
|
||||
ControlledAlarmClock* alarm);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_TEST_CREATE_TIME_CONTROLLER_H_
|
76
api/test/create_time_controller_unittest.cc
Normal file
76
api/test/create_time_controller_unittest.cc
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2019 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 "api/test/create_time_controller.h"
|
||||
|
||||
#include "api/test/time_controller.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
class FakeAlarm : public ControlledAlarmClock {
|
||||
public:
|
||||
explicit FakeAlarm(Timestamp start_time);
|
||||
|
||||
Clock* GetClock() override;
|
||||
bool ScheduleAlarmAt(Timestamp deadline) override;
|
||||
void SetCallback(std::function<void()> callback) override;
|
||||
void Sleep(TimeDelta duration) override;
|
||||
|
||||
private:
|
||||
SimulatedClock clock_;
|
||||
Timestamp deadline_;
|
||||
std::function<void()> callback_;
|
||||
};
|
||||
|
||||
FakeAlarm::FakeAlarm(Timestamp start_time)
|
||||
: clock_(start_time),
|
||||
deadline_(Timestamp::PlusInfinity()),
|
||||
callback_([] {}) {}
|
||||
|
||||
Clock* FakeAlarm::GetClock() {
|
||||
return &clock_;
|
||||
}
|
||||
|
||||
bool FakeAlarm::ScheduleAlarmAt(Timestamp deadline) {
|
||||
if (deadline < deadline_) {
|
||||
deadline_ = deadline;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FakeAlarm::SetCallback(std::function<void()> callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void FakeAlarm::Sleep(TimeDelta duration) {
|
||||
Timestamp end_time = clock_.CurrentTime() + duration;
|
||||
|
||||
while (deadline_ <= end_time) {
|
||||
clock_.AdvanceTime(deadline_ - clock_.CurrentTime());
|
||||
deadline_ = Timestamp::PlusInfinity();
|
||||
callback_();
|
||||
}
|
||||
|
||||
clock_.AdvanceTime(end_time - clock_.CurrentTime());
|
||||
}
|
||||
|
||||
TEST(CreateTimeControllerTest, CreatesNonNullController) {
|
||||
FakeAlarm alarm(Timestamp::ms(100));
|
||||
EXPECT_NE(CreateTimeController(&alarm), nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
@ -7,15 +7,17 @@
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef TEST_TIME_CONTROLLER_TIME_CONTROLLER_H_
|
||||
#define TEST_TIME_CONTROLLER_TIME_CONTROLLER_H_
|
||||
#ifndef API_TEST_TIME_CONTROLLER_H_
|
||||
#define API_TEST_TIME_CONTROLLER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "rtc_base/synchronization/yield_policy.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -42,6 +44,32 @@ class TimeController {
|
||||
// might yield to execute other tasks. This allows doing blocking waits on
|
||||
// tasks on other task queues froma a task queue without deadlocking.
|
||||
virtual void InvokeWithControlledYield(std::function<void()> closure) = 0;
|
||||
// Returns a YieldInterface which can be installed as a ScopedYieldPolicy.
|
||||
virtual rtc::YieldInterface* YieldInterface() = 0;
|
||||
};
|
||||
|
||||
// Interface for telling time, scheduling an event to fire at a particular time,
|
||||
// and waiting for time to pass.
|
||||
class ControlledAlarmClock {
|
||||
public:
|
||||
virtual ~ControlledAlarmClock() = default;
|
||||
|
||||
// Gets a clock that tells the alarm clock's notion of time.
|
||||
virtual Clock* GetClock() = 0;
|
||||
|
||||
// Schedules the alarm to fire at |deadline|.
|
||||
// An alarm clock only supports one deadline. Calls to |ScheduleAlarmAt| with
|
||||
// an earlier deadline will reset the alarm to fire earlier.Calls to
|
||||
// |ScheduleAlarmAt| with a later deadline are ignored. Returns true if the
|
||||
// deadline changed, false otherwise.
|
||||
virtual bool ScheduleAlarmAt(Timestamp deadline) = 0;
|
||||
|
||||
// Sets the callback that should be run when the alarm fires.
|
||||
virtual void SetCallback(std::function<void()> callback) = 0;
|
||||
|
||||
// Waits for |duration| to pass, according to the alarm clock.
|
||||
virtual void Sleep(TimeDelta duration) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // TEST_TIME_CONTROLLER_TIME_CONTROLLER_H_
|
||||
#endif // API_TEST_TIME_CONTROLLER_H_
|
@ -41,6 +41,7 @@ rtc_library("emulated_network") {
|
||||
deps = [
|
||||
"../../api:network_emulation_manager_api",
|
||||
"../../api:simulated_network_api",
|
||||
"../../api:time_controller",
|
||||
"../../api/units:data_rate",
|
||||
"../../api/units:data_size",
|
||||
"../../api/units:time_delta",
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "api/test/network_emulation_manager.h"
|
||||
#include "api/test/simulated_network.h"
|
||||
#include "api/test/time_controller.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/logging.h"
|
||||
@ -33,7 +34,6 @@
|
||||
#include "test/network/network_emulation.h"
|
||||
#include "test/network/simulated_network_node.h"
|
||||
#include "test/network/traffic_route.h"
|
||||
#include "test/time_controller/time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
@ -84,6 +84,8 @@ if (rtc_include_tests) {
|
||||
"../../api:libjingle_peerconnection_api",
|
||||
"../../api:rtc_event_log_output_file",
|
||||
"../../api:rtp_parameters",
|
||||
"../../api:time_controller",
|
||||
"../../api:time_controller",
|
||||
"../../api:transport_api",
|
||||
"../../api/audio_codecs:builtin_audio_decoder_factory",
|
||||
"../../api/audio_codecs:builtin_audio_encoder_factory",
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "api/test/time_controller.h"
|
||||
#include "call/call.h"
|
||||
#include "modules/audio_device/include/test_audio_device.h"
|
||||
#include "modules/congestion_controller/goog_cc/test/goog_cc_printer.h"
|
||||
@ -28,7 +29,6 @@
|
||||
#include "test/scenario/column_printer.h"
|
||||
#include "test/scenario/network_node.h"
|
||||
#include "test/scenario/scenario_config.h"
|
||||
#include "test/time_controller/time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test/time_controller.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/fake_clock.h"
|
||||
#include "rtc_base/task_queue.h"
|
||||
@ -26,7 +27,6 @@
|
||||
#include "test/scenario/network_node.h"
|
||||
#include "test/scenario/scenario_config.h"
|
||||
#include "test/scenario/video_stream.h"
|
||||
#include "test/time_controller/time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
@ -12,14 +12,16 @@ if (rtc_include_tests) {
|
||||
rtc_library("time_controller") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"external_time_controller.cc",
|
||||
"external_time_controller.h",
|
||||
"real_time_controller.cc",
|
||||
"real_time_controller.h",
|
||||
"simulated_time_controller.cc",
|
||||
"simulated_time_controller.h",
|
||||
"time_controller.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"../../api:time_controller",
|
||||
"../../api/task_queue",
|
||||
"../../api/task_queue:default_task_queue_factory",
|
||||
"../../api/units:time_delta",
|
||||
@ -39,6 +41,7 @@ if (rtc_include_tests) {
|
||||
rtc_library("time_controller_unittests") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"external_time_controller_unittest.cc",
|
||||
"simulated_time_controller_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
|
226
test/time_controller/external_time_controller.cc
Normal file
226
test/time_controller/external_time_controller.cc
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright 2019 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/time_controller/external_time_controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/task_queue/queued_task.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/include/module.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "rtc_base/synchronization/yield_policy.h"
|
||||
#include "test/time_controller/simulated_time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Wraps a ProcessThread so that it can reschedule the time controller whenever
|
||||
// an external call changes the ProcessThread's state. For example, when a new
|
||||
// module is registered, the ProcessThread may need to be called sooner than the
|
||||
// time controller's currently-scheduled deadline.
|
||||
class ExternalTimeController::ProcessThreadWrapper : public ProcessThread {
|
||||
public:
|
||||
ProcessThreadWrapper(ExternalTimeController* parent,
|
||||
std::unique_ptr<ProcessThread> thread)
|
||||
: parent_(parent), thread_(std::move(thread)) {}
|
||||
|
||||
void Start() override {
|
||||
parent_->UpdateTime();
|
||||
thread_->Start();
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void Stop() override {
|
||||
parent_->UpdateTime();
|
||||
thread_->Stop();
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void WakeUp(Module* module) override {
|
||||
parent_->UpdateTime();
|
||||
thread_->WakeUp(GetWrapper(module));
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void PostTask(std::unique_ptr<QueuedTask> task) override {
|
||||
parent_->UpdateTime();
|
||||
thread_->PostTask(std::move(task));
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void RegisterModule(Module* module, const rtc::Location& from) override {
|
||||
parent_->UpdateTime();
|
||||
module_wrappers_.emplace(module, new ModuleWrapper(module, this));
|
||||
thread_->RegisterModule(GetWrapper(module), from);
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void DeRegisterModule(Module* module) override {
|
||||
parent_->UpdateTime();
|
||||
thread_->DeRegisterModule(GetWrapper(module));
|
||||
parent_->ScheduleNext();
|
||||
module_wrappers_.erase(module);
|
||||
}
|
||||
|
||||
private:
|
||||
class ModuleWrapper : public Module {
|
||||
public:
|
||||
ModuleWrapper(Module* module, ProcessThreadWrapper* thread)
|
||||
: module_(module), thread_(thread) {}
|
||||
|
||||
int64_t TimeUntilNextProcess() override {
|
||||
return module_->TimeUntilNextProcess();
|
||||
}
|
||||
|
||||
void Process() override { module_->Process(); }
|
||||
|
||||
void ProcessThreadAttached(ProcessThread* process_thread) override {
|
||||
if (process_thread) {
|
||||
module_->ProcessThreadAttached(thread_);
|
||||
} else {
|
||||
module_->ProcessThreadAttached(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Module* module_;
|
||||
ProcessThreadWrapper* thread_;
|
||||
};
|
||||
|
||||
ModuleWrapper* GetWrapper(Module* module) {
|
||||
auto it = module_wrappers_.find(module);
|
||||
RTC_DCHECK(it != module_wrappers_.end());
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
ExternalTimeController* const parent_;
|
||||
std::unique_ptr<ProcessThread> thread_;
|
||||
std::map<Module*, std::unique_ptr<ModuleWrapper>> module_wrappers_;
|
||||
};
|
||||
|
||||
// Wraps a TaskQueue so that it can reschedule the time controller whenever
|
||||
// an external call schedules a new task.
|
||||
class ExternalTimeController::TaskQueueWrapper : public TaskQueueBase {
|
||||
public:
|
||||
TaskQueueWrapper(ExternalTimeController* parent,
|
||||
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> base)
|
||||
: parent_(parent), base_(std::move(base)) {}
|
||||
|
||||
void PostTask(std::unique_ptr<QueuedTask> task) override {
|
||||
parent_->UpdateTime();
|
||||
base_->PostTask(std::make_unique<TaskWrapper>(std::move(task), this));
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t ms) override {
|
||||
parent_->UpdateTime();
|
||||
base_->PostDelayedTask(std::make_unique<TaskWrapper>(std::move(task), this),
|
||||
ms);
|
||||
parent_->ScheduleNext();
|
||||
}
|
||||
|
||||
void Delete() override { delete this; }
|
||||
|
||||
private:
|
||||
class TaskWrapper : public QueuedTask {
|
||||
public:
|
||||
TaskWrapper(std::unique_ptr<QueuedTask> task, TaskQueueWrapper* queue)
|
||||
: task_(std::move(task)), queue_(queue) {}
|
||||
|
||||
bool Run() override {
|
||||
CurrentTaskQueueSetter current(queue_);
|
||||
if (!task_->Run()) {
|
||||
task_.release();
|
||||
}
|
||||
// The wrapper should always be deleted, even if it releases the inner
|
||||
// task, in order to avoid leaking wrappers.
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<QueuedTask> task_;
|
||||
TaskQueueWrapper* queue_;
|
||||
};
|
||||
|
||||
ExternalTimeController* const parent_;
|
||||
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> base_;
|
||||
};
|
||||
|
||||
ExternalTimeController::ExternalTimeController(ControlledAlarmClock* alarm)
|
||||
: alarm_(alarm), impl_(alarm_->GetClock()->CurrentTime()) {
|
||||
global_clock_.SetTime(alarm_->GetClock()->CurrentTime());
|
||||
alarm_->SetCallback([this] { Run(); });
|
||||
}
|
||||
|
||||
Clock* ExternalTimeController::GetClock() {
|
||||
return alarm_->GetClock();
|
||||
}
|
||||
|
||||
TaskQueueFactory* ExternalTimeController::GetTaskQueueFactory() {
|
||||
return this;
|
||||
}
|
||||
|
||||
std::unique_ptr<ProcessThread> ExternalTimeController::CreateProcessThread(
|
||||
const char* thread_name) {
|
||||
return std::make_unique<ProcessThreadWrapper>(
|
||||
this, impl_.CreateProcessThread(thread_name));
|
||||
}
|
||||
|
||||
void ExternalTimeController::Sleep(TimeDelta duration) {
|
||||
alarm_->Sleep(duration);
|
||||
}
|
||||
|
||||
void ExternalTimeController::InvokeWithControlledYield(
|
||||
std::function<void()> closure) {
|
||||
rtc::ScopedYieldPolicy policy(YieldInterface());
|
||||
closure();
|
||||
}
|
||||
|
||||
rtc::YieldInterface* ExternalTimeController::YieldInterface() {
|
||||
return &impl_;
|
||||
}
|
||||
|
||||
std::unique_ptr<TaskQueueBase, TaskQueueDeleter>
|
||||
ExternalTimeController::CreateTaskQueue(
|
||||
absl::string_view name,
|
||||
TaskQueueFactory::Priority priority) const {
|
||||
return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>(
|
||||
new TaskQueueWrapper(const_cast<ExternalTimeController*>(this),
|
||||
impl_.CreateTaskQueue(name, priority)));
|
||||
}
|
||||
|
||||
void ExternalTimeController::Run() {
|
||||
rtc::ScopedYieldPolicy yield_policy(&impl_);
|
||||
UpdateTime();
|
||||
impl_.RunReadyRunners();
|
||||
ScheduleNext();
|
||||
}
|
||||
|
||||
void ExternalTimeController::UpdateTime() {
|
||||
Timestamp now = alarm_->GetClock()->CurrentTime();
|
||||
impl_.AdvanceTime(now);
|
||||
global_clock_.SetTime(now);
|
||||
}
|
||||
|
||||
void ExternalTimeController::ScheduleNext() {
|
||||
RTC_DCHECK_EQ(impl_.CurrentTime(), alarm_->GetClock()->CurrentTime());
|
||||
TimeDelta delay =
|
||||
std::max(impl_.NextRunTime() - impl_.CurrentTime(), TimeDelta::Zero());
|
||||
if (delay.IsFinite()) {
|
||||
alarm_->ScheduleAlarmAt(alarm_->GetClock()->CurrentTime() + delay);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
70
test/time_controller/external_time_controller.h
Normal file
70
test/time_controller/external_time_controller.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
#ifndef TEST_TIME_CONTROLLER_EXTERNAL_TIME_CONTROLLER_H_
|
||||
#define TEST_TIME_CONTROLLER_EXTERNAL_TIME_CONTROLLER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/test/time_controller.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/time_controller/simulated_time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TimeController implementation built on an external controlled alarm.
|
||||
// This implementation is used to delegate scheduling and execution to an
|
||||
// external run loop.
|
||||
class ExternalTimeController : public TimeController, public TaskQueueFactory {
|
||||
public:
|
||||
explicit ExternalTimeController(ControlledAlarmClock* alarm);
|
||||
|
||||
// Implementation of TimeController.
|
||||
Clock* GetClock() override;
|
||||
TaskQueueFactory* GetTaskQueueFactory() override;
|
||||
std::unique_ptr<ProcessThread> CreateProcessThread(
|
||||
const char* thread_name) override;
|
||||
void Sleep(TimeDelta duration) override;
|
||||
void InvokeWithControlledYield(std::function<void()> closure) override;
|
||||
rtc::YieldInterface* YieldInterface() override;
|
||||
|
||||
// Implementation of TaskQueueFactory.
|
||||
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
|
||||
absl::string_view name,
|
||||
TaskQueueFactory::Priority priority) const override;
|
||||
|
||||
private:
|
||||
class ProcessThreadWrapper;
|
||||
class TaskQueueWrapper;
|
||||
|
||||
// Executes any tasks scheduled at or before the current time. May call
|
||||
// |ScheduleNext| to schedule the next call to |Run|.
|
||||
void Run();
|
||||
|
||||
void UpdateTime();
|
||||
void ScheduleNext();
|
||||
|
||||
ControlledAlarmClock* alarm_;
|
||||
sim_time_impl::SimulatedTimeControllerImpl impl_;
|
||||
|
||||
// Overrides the global rtc::Clock to ensure that it reports the same times as
|
||||
// the time controller.
|
||||
rtc::ScopedBaseFakeClock global_clock_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TEST_TIME_CONTROLLER_EXTERNAL_TIME_CONTROLLER_H_
|
181
test/time_controller/external_time_controller_unittest.cc
Normal file
181
test/time_controller/external_time_controller_unittest.cc
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2019 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/time_controller/external_time_controller.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/event.h"
|
||||
#include "rtc_base/task_queue.h"
|
||||
#include "rtc_base/task_utils/repeating_task.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
// NOTE: Since these tests rely on real time behavior, they will be flaky
|
||||
// if run on heavily loaded systems.
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::MockFunction;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
constexpr Timestamp kStartTime = Timestamp::Seconds<1000>();
|
||||
|
||||
class FakeAlarm : public ControlledAlarmClock {
|
||||
public:
|
||||
explicit FakeAlarm(Timestamp start_time);
|
||||
|
||||
Clock* GetClock() override;
|
||||
bool ScheduleAlarmAt(Timestamp deadline) override;
|
||||
void SetCallback(std::function<void()> callback) override;
|
||||
void Sleep(TimeDelta duration) override;
|
||||
|
||||
private:
|
||||
SimulatedClock clock_;
|
||||
Timestamp deadline_;
|
||||
std::function<void()> callback_;
|
||||
};
|
||||
|
||||
FakeAlarm::FakeAlarm(Timestamp start_time)
|
||||
: clock_(start_time),
|
||||
deadline_(Timestamp::PlusInfinity()),
|
||||
callback_([] {}) {}
|
||||
|
||||
Clock* FakeAlarm::GetClock() {
|
||||
return &clock_;
|
||||
}
|
||||
|
||||
bool FakeAlarm::ScheduleAlarmAt(Timestamp deadline) {
|
||||
if (deadline < deadline_) {
|
||||
deadline_ = deadline;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FakeAlarm::SetCallback(std::function<void()> callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void FakeAlarm::Sleep(TimeDelta duration) {
|
||||
Timestamp end_time = clock_.CurrentTime() + duration;
|
||||
|
||||
while (deadline_ <= end_time) {
|
||||
clock_.AdvanceTime(deadline_ - clock_.CurrentTime());
|
||||
deadline_ = Timestamp::PlusInfinity();
|
||||
callback_();
|
||||
}
|
||||
|
||||
clock_.AdvanceTime(end_time - clock_.CurrentTime());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ExternalTimeControllerTest, TaskIsStoppedOnStop) {
|
||||
const TimeDelta kShortInterval = TimeDelta::ms(5);
|
||||
const TimeDelta kLongInterval = TimeDelta::ms(20);
|
||||
const int kShortIntervalCount = 4;
|
||||
const int kMargin = 1;
|
||||
FakeAlarm alarm(kStartTime);
|
||||
ExternalTimeController time_simulation(&alarm);
|
||||
rtc::TaskQueue task_queue(
|
||||
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
|
||||
"TestQueue", TaskQueueFactory::Priority::NORMAL));
|
||||
std::atomic_int counter(0);
|
||||
auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
|
||||
if (++counter >= kShortIntervalCount)
|
||||
return kLongInterval;
|
||||
return kShortInterval;
|
||||
});
|
||||
// Sleep long enough to go through the initial phase.
|
||||
time_simulation.Sleep(kShortInterval * (kShortIntervalCount + kMargin));
|
||||
EXPECT_EQ(counter.load(), kShortIntervalCount);
|
||||
|
||||
task_queue.PostTask(
|
||||
[handle = std::move(handle)]() mutable { handle.Stop(); });
|
||||
|
||||
// Sleep long enough that the task would run at least once more if not
|
||||
// stopped.
|
||||
time_simulation.Sleep(kLongInterval * 2);
|
||||
EXPECT_EQ(counter.load(), kShortIntervalCount);
|
||||
}
|
||||
|
||||
TEST(ExternalTimeControllerTest, TaskCanStopItself) {
|
||||
std::atomic_int counter(0);
|
||||
FakeAlarm alarm(kStartTime);
|
||||
ExternalTimeController time_simulation(&alarm);
|
||||
rtc::TaskQueue task_queue(
|
||||
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
|
||||
"TestQueue", TaskQueueFactory::Priority::NORMAL));
|
||||
|
||||
RepeatingTaskHandle handle;
|
||||
task_queue.PostTask([&] {
|
||||
handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
|
||||
++counter;
|
||||
handle.Stop();
|
||||
return TimeDelta::ms(2);
|
||||
});
|
||||
});
|
||||
time_simulation.Sleep(TimeDelta::ms(10));
|
||||
EXPECT_EQ(counter.load(), 1);
|
||||
}
|
||||
|
||||
TEST(ExternalTimeControllerTest, YieldForTask) {
|
||||
FakeAlarm alarm(kStartTime);
|
||||
ExternalTimeController time_simulation(&alarm);
|
||||
|
||||
rtc::TaskQueue task_queue(
|
||||
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
|
||||
"TestQueue", TaskQueueFactory::Priority::NORMAL));
|
||||
|
||||
time_simulation.InvokeWithControlledYield([&] {
|
||||
rtc::Event event;
|
||||
task_queue.PostTask([&] { event.Set(); });
|
||||
EXPECT_TRUE(event.Wait(200));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(ExternalTimeControllerTest, TasksYieldToEachOther) {
|
||||
FakeAlarm alarm(kStartTime);
|
||||
ExternalTimeController time_simulation(&alarm);
|
||||
|
||||
rtc::TaskQueue task_queue(
|
||||
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
|
||||
"TestQueue", TaskQueueFactory::Priority::NORMAL));
|
||||
rtc::TaskQueue other_queue(
|
||||
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
|
||||
"OtherQueue", TaskQueueFactory::Priority::NORMAL));
|
||||
|
||||
task_queue.PostTask([&] {
|
||||
rtc::Event event;
|
||||
other_queue.PostTask([&] { event.Set(); });
|
||||
EXPECT_TRUE(event.Wait(200));
|
||||
});
|
||||
|
||||
time_simulation.Sleep(TimeDelta::ms(300));
|
||||
}
|
||||
|
||||
TEST(ExternalTimeControllerTest, CurrentTaskQueue) {
|
||||
FakeAlarm alarm(kStartTime);
|
||||
ExternalTimeController time_simulation(&alarm);
|
||||
|
||||
rtc::TaskQueue task_queue(
|
||||
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
|
||||
"TestQueue", TaskQueueFactory::Priority::NORMAL));
|
||||
|
||||
task_queue.PostTask([&] { EXPECT_TRUE(task_queue.IsCurrent()); });
|
||||
|
||||
time_simulation.Sleep(TimeDelta::ms(10));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -39,6 +39,10 @@ void RealTimeController::InvokeWithControlledYield(
|
||||
closure();
|
||||
}
|
||||
|
||||
rtc::YieldInterface* RealTimeController::YieldInterface() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RealTimeController* GlobalRealTimeController() {
|
||||
static RealTimeController* time_controller = new RealTimeController();
|
||||
return time_controller;
|
||||
|
@ -14,10 +14,10 @@
|
||||
#include <memory>
|
||||
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/test/time_controller.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/time_controller/time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
class RealTimeController : public TimeController {
|
||||
@ -30,6 +30,7 @@ class RealTimeController : public TimeController {
|
||||
const char* thread_name) override;
|
||||
void Sleep(TimeDelta duration) override;
|
||||
void InvokeWithControlledYield(std::function<void()> closure) override;
|
||||
rtc::YieldInterface* YieldInterface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TaskQueueFactory> task_queue_factory_;
|
||||
|
@ -440,6 +440,10 @@ void GlobalSimulatedTimeController::InvokeWithControlledYield(
|
||||
closure();
|
||||
}
|
||||
|
||||
rtc::YieldInterface* GlobalSimulatedTimeController::YieldInterface() {
|
||||
return &impl_;
|
||||
}
|
||||
|
||||
// namespace sim_time_impl
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test/time_controller.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/include/module.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
@ -24,7 +25,6 @@
|
||||
#include "rtc_base/platform_thread_types.h"
|
||||
#include "rtc_base/synchronization/yield_policy.h"
|
||||
#include "rtc_base/thread_checker.h"
|
||||
#include "test/time_controller/time_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -92,6 +92,7 @@ class GlobalSimulatedTimeController : public TimeController {
|
||||
const char* thread_name) override;
|
||||
void Sleep(TimeDelta duration) override;
|
||||
void InvokeWithControlledYield(std::function<void()> closure) override;
|
||||
rtc::YieldInterface* YieldInterface() override;
|
||||
|
||||
private:
|
||||
rtc::ScopedBaseFakeClock global_clock_;
|
||||
|
Reference in New Issue
Block a user