Commit 5e2df665 authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

Add MOCK_TIME mode to ScopedTaskEnvironment :)

Taking advantage of the new kBoundToThread mode on
TestMockTimeTaskRunner.

This change also required tweaking the
ScopedTaskEnvironment::RunUntilIdle() logic as RunLoop().Run() on
TestMockTimeTaskRunner results in advancing time when there's no
request to quit-when-idle which is undesired here. New logic gets rid
of need for |on_queue_empty_closure_| and I think is simpler overall.
As of patch set 20, this new RunUntilIdle() logic also avoids using
TaskScheduler::FlushForTesting() as that can result in hangs should a
TaskScheduler task synchronously block on the main thread.

R=fdoray@chromium.org
TBR=gab@chromium.org (IWYU fixes)

Bug: 708584
Change-Id: I76ba55ec64d398151420379d3fcdcd5186fbceb8
Reviewed-on: https://chromium-review.googlesource.com/638550
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#518433}
parent 78294a3b
......@@ -13,6 +13,7 @@
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
......
......@@ -246,7 +246,7 @@ void TaskTracker::Shutdown() {
void TaskTracker::Flush() {
AutoSchedulerLock auto_lock(flush_lock_);
while (subtle::Acquire_Load(&num_pending_undelayed_tasks_) != 0 &&
while (subtle::Acquire_Load(&num_incomplete_undelayed_tasks_) != 0 &&
!IsShutdownComplete()) {
flush_cv_->Wait();
}
......@@ -259,7 +259,7 @@ bool TaskTracker::WillPostTask(const Task* task) {
return false;
if (task->delayed_run_time.is_null())
subtle::NoBarrier_AtomicIncrement(&num_pending_undelayed_tasks_, 1);
subtle::NoBarrier_AtomicIncrement(&num_incomplete_undelayed_tasks_, 1);
debug::TaskAnnotator task_annotator;
task_annotator.DidQueueTask(kQueueFunctionName, *task);
......@@ -313,9 +313,7 @@ scoped_refptr<Sequence> TaskTracker::RunNextTask(
AfterRunTask(shutdown_behavior);
if (!is_delayed)
DecrementNumPendingUndelayedTasks();
OnRunNextTaskCompleted();
DecrementNumIncompleteUndelayedTasks();
const bool sequence_is_empty_after_pop = sequence->Pop();
......@@ -476,8 +474,8 @@ bool TaskTracker::IsPostingBlockShutdownTaskAfterShutdownAllowed() {
}
#endif
int TaskTracker::GetNumPendingUndelayedTasksForTesting() const {
return subtle::NoBarrier_Load(&num_pending_undelayed_tasks_);
int TaskTracker::GetNumIncompleteUndelayedTasksForTesting() const {
return subtle::NoBarrier_Load(&num_incomplete_undelayed_tasks_);
}
bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
......@@ -595,11 +593,11 @@ void TaskTracker::OnBlockingShutdownTasksComplete() {
shutdown_event_->Signal();
}
void TaskTracker::DecrementNumPendingUndelayedTasks() {
const auto new_num_pending_undelayed_tasks =
subtle::Barrier_AtomicIncrement(&num_pending_undelayed_tasks_, -1);
DCHECK_GE(new_num_pending_undelayed_tasks, 0);
if (new_num_pending_undelayed_tasks == 0) {
void TaskTracker::DecrementNumIncompleteUndelayedTasks() {
const auto new_num_incomplete_undelayed_tasks =
subtle::Barrier_AtomicIncrement(&num_incomplete_undelayed_tasks_, -1);
DCHECK_GE(new_num_incomplete_undelayed_tasks, 0);
if (new_num_incomplete_undelayed_tasks == 0) {
AutoSchedulerLock auto_lock(flush_lock_);
flush_cv_->Signal();
}
......
......@@ -99,7 +99,7 @@ class BASE_EXPORT TaskTracker {
// This can only be called once.
void Shutdown();
// Waits until there are no pending undelayed tasks. May be called in tests
// Waits until there are no incomplete undelayed tasks. May be called in tests
// to validate that a condition is met after all undelayed tasks have run.
//
// Does not wait for delayed tasks. Waits for undelayed tasks posted from
......@@ -166,13 +166,9 @@ class BASE_EXPORT TaskTracker {
virtual bool IsPostingBlockShutdownTaskAfterShutdownAllowed();
#endif
// Called at the very end of RunNextTask() after the completion of all task
// metrics accounting.
virtual void OnRunNextTaskCompleted() {}
// Returns the number of undelayed tasks that haven't completed their
// execution.
int GetNumPendingUndelayedTasksForTesting() const;
// execution (still queued or in progress).
int GetNumIncompleteUndelayedTasksForTesting() const;
private:
class State;
......@@ -199,9 +195,9 @@ class BASE_EXPORT TaskTracker {
// shutdown has started.
void OnBlockingShutdownTasksComplete();
// Decrements the number of pending undelayed tasks and signals |flush_cv_| if
// it reaches zero.
void DecrementNumPendingUndelayedTasks();
// Decrements the number of incomplete undelayed tasks and signals |flush_cv_|
// if it reaches zero.
void DecrementNumIncompleteUndelayedTasks();
// To be called after running a background task from |just_ran_sequence|.
// Performs the following actions:
......@@ -233,15 +229,15 @@ class BASE_EXPORT TaskTracker {
// decremented with a memory barrier after a task runs. Is accessed with an
// acquire memory barrier in Flush(). The memory barriers ensure that the
// memory written by flushed tasks is visible when Flush() returns.
subtle::Atomic32 num_pending_undelayed_tasks_ = 0;
subtle::Atomic32 num_incomplete_undelayed_tasks_ = 0;
// Lock associated with |flush_cv_|. Partially synchronizes access to
// |num_pending_undelayed_tasks_|. Full synchronization isn't needed because
// it's atomic, but synchronization is needed to coordinate waking and
// |num_incomplete_undelayed_tasks_|. Full synchronization isn't needed
// because it's atomic, but synchronization is needed to coordinate waking and
// sleeping at the right time.
mutable SchedulerLock flush_lock_;
// Signaled when |num_pending_undelayed_tasks_| is zero or when shutdown
// Signaled when |num_incomplete_undelayed_tasks_| is zero or when shutdown
// completes.
const std::unique_ptr<ConditionVariable> flush_cv_;
......
This diff is collapsed.
......@@ -6,13 +6,15 @@
#define BASE_TEST_SCOPED_TASK_ENVIRONMENT_H_
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/task_scheduler/lazy_task_runner.h"
namespace base {
class MessageLoop;
class TaskScheduler;
class TestMockTimeTaskRunner;
namespace test {
......@@ -58,6 +60,12 @@ class ScopedTaskEnvironment {
enum class MainThreadType {
// The main thread doesn't pump system messages.
DEFAULT,
// The main thread doesn't pump system messages and uses a mock clock for
// delayed tasks (controllable via FastForward*() methods).
// TODO(gab): Make this the default |main_thread_type|.
// TODO(gab): Also mock the TaskScheduler's clock simultaneously (this
// currently only mocks the main thread's clock).
MOCK_TIME,
// The main thread pumps UI messages.
UI,
// The main thread pumps asynchronous IO messages.
......@@ -85,18 +93,31 @@ class ScopedTaskEnvironment {
scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner();
// Runs tasks until both the (Thread|Sequenced)TaskRunnerHandle and the
// TaskScheduler queues are empty.
// TaskScheduler's non-delayed queues are empty.
void RunUntilIdle();
// Only valid for instances with a MOCK_TIME MainThreadType.
// Fast-forwards virtual time by |delta|, causing all tasks on the main thread
// with a remaining delay less than or equal to |delta| to be executed.
// |delta| must be non-negative.
void FastForwardBy(TimeDelta delta);
// Only valid for instances with a MOCK_TIME MainThreadType.
// Fast-forwards virtual time just until all tasks are executed on the main
// thread.
void FastForwardUntilNoTasksRemain();
private:
class TestTaskTracker;
const ExecutionMode execution_control_mode_;
// Note: |message_loop_| is an implementation detail and will be replaced in
// the future, do NOT rely on the presence of a MessageLoop beyond
// (Thread|Sequenced)TaskRunnerHandle and RunLoop.
MessageLoop message_loop_;
// Exactly one of these will be non-null to provide the task environment on
// the main thread. Users of this class should NOT rely on the presence of a
// MessageLoop beyond (Thread|Sequenced)TaskRunnerHandle and RunLoop as
// the backing implementation of each MainThreadType may change over time.
const std::unique_ptr<MessageLoop> message_loop_;
const scoped_refptr<TestMockTimeTaskRunner> mock_time_task_runner_;
const TaskScheduler* task_scheduler_ = nullptr;
......
......@@ -4,6 +4,7 @@
#include "base/test/scoped_task_environment.h"
#include "base/atomicops.h"
#include "base/bind.h"
#include "base/synchronization/atomic_flag.h"
#include "base/synchronization/waitable_event.h"
......@@ -18,6 +19,9 @@ namespace test {
namespace {
class ScopedTaskEnvironmentTest
: public testing::TestWithParam<ScopedTaskEnvironment::MainThreadType> {};
void VerifyRunUntilIdleDidNotReturnAndSetFlag(
AtomicFlag* run_until_idle_returned,
AtomicFlag* task_ran) {
......@@ -26,10 +30,11 @@ void VerifyRunUntilIdleDidNotReturnAndSetFlag(
}
void RunUntilIdleTest(
ScopedTaskEnvironment::MainThreadType main_thread_type,
ScopedTaskEnvironment::ExecutionMode execution_control_mode) {
AtomicFlag run_until_idle_returned;
ScopedTaskEnvironment scoped_task_environment(
ScopedTaskEnvironment::MainThreadType::DEFAULT, execution_control_mode);
ScopedTaskEnvironment scoped_task_environment(main_thread_type,
execution_control_mode);
AtomicFlag first_main_thread_task_ran;
ThreadTaskRunnerHandle::Get()->PostTask(
......@@ -63,20 +68,19 @@ void RunUntilIdleTest(
} // namespace
TEST(ScopedTaskEnvironmentTest, QueuedRunUntilIdle) {
RunUntilIdleTest(ScopedTaskEnvironment::ExecutionMode::QUEUED);
TEST_P(ScopedTaskEnvironmentTest, QueuedRunUntilIdle) {
RunUntilIdleTest(GetParam(), ScopedTaskEnvironment::ExecutionMode::QUEUED);
}
TEST(ScopedTaskEnvironmentTest, AsyncRunUntilIdle) {
RunUntilIdleTest(ScopedTaskEnvironment::ExecutionMode::ASYNC);
TEST_P(ScopedTaskEnvironmentTest, AsyncRunUntilIdle) {
RunUntilIdleTest(GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
}
// Verify that tasks posted to an ExecutionMode::QUEUED ScopedTaskEnvironment do
// not run outside of RunUntilIdle().
TEST(ScopedTaskEnvironmentTest, QueuedTasksDoNotRunOutsideOfRunUntilIdle) {
TEST_P(ScopedTaskEnvironmentTest, QueuedTasksDoNotRunOutsideOfRunUntilIdle) {
ScopedTaskEnvironment scoped_task_environment(
ScopedTaskEnvironment::MainThreadType::DEFAULT,
ScopedTaskEnvironment::ExecutionMode::QUEUED);
GetParam(), ScopedTaskEnvironment::ExecutionMode::QUEUED);
AtomicFlag run_until_idle_called;
PostTask(FROM_HERE, BindOnce(
......@@ -101,10 +105,9 @@ TEST(ScopedTaskEnvironmentTest, QueuedTasksDoNotRunOutsideOfRunUntilIdle) {
// Verify that a task posted to an ExecutionMode::ASYNC ScopedTaskEnvironment
// can run without a call to RunUntilIdle().
TEST(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePosted) {
TEST_P(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePosted) {
ScopedTaskEnvironment scoped_task_environment(
ScopedTaskEnvironment::MainThreadType::DEFAULT,
ScopedTaskEnvironment::ExecutionMode::ASYNC);
GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
......@@ -117,10 +120,10 @@ TEST(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePosted) {
// Verify that a task posted to an ExecutionMode::ASYNC ScopedTaskEnvironment
// after a call to RunUntilIdle() can run without another call to
// RunUntilIdle().
TEST(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePostedAfterRunUntilIdle) {
TEST_P(ScopedTaskEnvironmentTest,
AsyncTasksRunAsTheyArePostedAfterRunUntilIdle) {
ScopedTaskEnvironment scoped_task_environment(
ScopedTaskEnvironment::MainThreadType::DEFAULT,
ScopedTaskEnvironment::ExecutionMode::ASYNC);
GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
scoped_task_environment.RunUntilIdle();
......@@ -132,5 +135,102 @@ TEST(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePostedAfterRunUntilIdle) {
task_ran.Wait();
}
TEST_P(ScopedTaskEnvironmentTest, DelayedTasks) {
ScopedTaskEnvironment scoped_task_environment(
GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
subtle::Atomic32 counter = 0;
// Should run only in MOCK_TIME environment when time is fast-forwarded.
ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
Bind(
[](subtle::Atomic32* counter) {
subtle::NoBarrier_AtomicIncrement(counter, 4);
},
Unretained(&counter)),
TimeDelta::FromDays(1));
// TODO(gab): This currently doesn't run because the TaskScheduler's clock
// isn't mocked but it should be.
PostDelayedTask(FROM_HERE,
Bind(
[](subtle::Atomic32* counter) {
subtle::NoBarrier_AtomicIncrement(counter, 128);
},
Unretained(&counter)),
TimeDelta::FromDays(1));
// Same as first task, longer delays to exercise
// FastForwardUntilNoTasksRemain().
ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
Bind(
[](subtle::Atomic32* counter) {
subtle::NoBarrier_AtomicIncrement(counter, 8);
},
Unretained(&counter)),
TimeDelta::FromDays(5));
ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
Bind(
[](subtle::Atomic32* counter) {
subtle::NoBarrier_AtomicIncrement(counter, 16);
},
Unretained(&counter)),
TimeDelta::FromDays(7));
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, Bind(
[](subtle::Atomic32* counter) {
subtle::NoBarrier_AtomicIncrement(counter, 1);
},
Unretained(&counter)));
PostTask(FROM_HERE, Bind(
[](subtle::Atomic32* counter) {
subtle::NoBarrier_AtomicIncrement(counter, 2);
},
Unretained(&counter)));
int expected_value = 0;
EXPECT_EQ(expected_value, counter);
// RunUntilIdle() should process non-delayed tasks only in all queues.
scoped_task_environment.RunUntilIdle();
expected_value += 1;
expected_value += 2;
EXPECT_EQ(expected_value, counter);
if (GetParam() == ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {
scoped_task_environment.FastForwardBy(TimeDelta::FromSeconds(1));
EXPECT_EQ(expected_value, counter);
scoped_task_environment.FastForwardBy(TimeDelta::FromDays(1));
expected_value += 4;
EXPECT_EQ(expected_value, counter);
scoped_task_environment.FastForwardUntilNoTasksRemain();
expected_value += 8;
expected_value += 16;
EXPECT_EQ(expected_value, counter);
}
}
INSTANTIATE_TEST_CASE_P(
MainThreadDefault,
ScopedTaskEnvironmentTest,
::testing::Values(ScopedTaskEnvironment::MainThreadType::DEFAULT));
INSTANTIATE_TEST_CASE_P(
MainThreadMockTime,
ScopedTaskEnvironmentTest,
::testing::Values(ScopedTaskEnvironment::MainThreadType::MOCK_TIME));
INSTANTIATE_TEST_CASE_P(
MainThreadUI,
ScopedTaskEnvironmentTest,
::testing::Values(ScopedTaskEnvironment::MainThreadType::UI));
INSTANTIATE_TEST_CASE_P(
MainThreadIO,
ScopedTaskEnvironmentTest,
::testing::Values(ScopedTaskEnvironment::MainThreadType::IO));
} // namespace test
} // namespace base
......@@ -28,6 +28,10 @@
using ::testing::NiceMock;
namespace base {
class RunLoop;
}
namespace media {
class FakeEncryptedMedia;
......
......@@ -4,6 +4,8 @@
#include "remoting/host/file_proxy_wrapper.h"
#include <queue>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/containers/queue.h"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment