blob: 30a0c62ef12c88816543d6bdc90d4435cf707b42 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/callback/scoped_task_runner.h"
#include <lib/async-testing/test_loop.h>
#include <pthread.h>
#include <thread>
#include <gtest/gtest.h>
#include "src/lib/fxl/macros.h"
namespace callback {
namespace {
constexpr zx::duration kInterval = zx::min(4); // to save the world
struct MoveOnly {
MoveOnly(bool b) : called(b) {}
MoveOnly(MoveOnly&&) = default;
bool called;
FXL_DISALLOW_COPY_AND_ASSIGN(MoveOnly);
};
TEST(ScopedTaskRunnerTest, RunsTaskInScope) {
bool called = false;
ScopedTaskRunner tasks;
tasks.MakeScoped([&] { called = true; })();
EXPECT_TRUE(called);
}
TEST(ScopedTaskRunnerTest, TaskWithArg) {
bool called = false;
ScopedTaskRunner tasks;
tasks.MakeScoped([&](bool value) { called = value; })(true);
EXPECT_TRUE(called);
}
TEST(ScopedTaskRunnerTest, TaskWithRefArg) {
bool called = false;
ScopedTaskRunner tasks;
tasks.MakeScoped([](bool& called) { called = true; })(called);
EXPECT_TRUE(called);
}
TEST(ScopedTaskRunnerTest, TaskWithMoveOnlyArg) {
bool called = false;
ScopedTaskRunner tasks;
tasks.MakeScoped([&](MoveOnly arg) { called = arg.called; })({true});
EXPECT_TRUE(called);
}
TEST(ScopedTaskRunnerTest, TaskWithMoveOnlyCapture) {
bool called = false;
ScopedTaskRunner tasks;
MoveOnly move_only{true};
tasks.MakeScoped([&called, move_only = std::move(move_only)] { called = move_only.called; })();
EXPECT_TRUE(called);
}
TEST(ScopedTaskRunnerTest, CancelsTaskOutOfScope) {
bool called = false;
fit::closure task;
{
ScopedTaskRunner tasks;
task = tasks.MakeScoped([&] { called = true; });
}
task();
EXPECT_FALSE(called);
}
TEST(ScopedTaskRunnerTest, ExplicitShutdown) {
bool called = false;
ScopedTaskRunner tasks;
auto task = tasks.MakeScoped([&] { called = true; });
tasks.ShutDown();
task();
EXPECT_FALSE(called);
}
TEST(ScopedTaskRunnerTest, MakeScopedAfterShutdown) {
bool called = false;
ScopedTaskRunner tasks;
tasks.ShutDown();
tasks.MakeScoped([&] { called = true; })();
EXPECT_FALSE(called);
}
TEST(ScopedTaskRunnerTest, Reset) {
bool beforeCalled = false, afterCalled = false;
ScopedTaskRunner tasks;
auto before = tasks.MakeScoped([&] { beforeCalled = true; });
tasks.Reset();
auto after = tasks.MakeScoped([&] { afterCalled = true; });
before();
after();
EXPECT_FALSE(beforeCalled);
EXPECT_TRUE(afterCalled);
}
TEST(ScopedTaskRunnerTest, DestroyDuringTaskOnThread) {
bool called = false;
auto* tasks = new ScopedTaskRunner;
tasks->MakeScoped([&] {
delete tasks;
called = true;
})();
EXPECT_TRUE(called);
}
TEST(ScopedTaskRunner, PostTaskAndFriends) {
async::TestLoop loop;
uint8_t called = 0;
auto increment_call = [&called] { ++called; };
ScopedTaskRunner task_runner(loop.dispatcher());
task_runner.PostTask(increment_call);
task_runner.PostDelayedTask(increment_call, zx::sec(0));
task_runner.PostTaskForTime(increment_call, zx::time(0));
loop.RunUntilIdle();
EXPECT_EQ(3u, called);
}
TEST(ScopedTaskRunner, PostTaskAndFriendsCancelOnDeletion) {
async::TestLoop loop;
uint8_t called = 0;
auto increment_call = [&called] { ++called; };
{
ScopedTaskRunner task_runner(loop.dispatcher());
task_runner.PostTask(increment_call);
task_runner.PostDelayedTask(increment_call, zx::sec(0));
task_runner.PostTaskForTime(increment_call, zx::time(0));
}
loop.RunUntilIdle();
EXPECT_EQ(0u, called);
}
TEST(ScopedTaskRunnerTest, PostPeriodicTask) {
async::TestLoop loop;
int called = 0;
{
ScopedTaskRunner tasks(loop.dispatcher());
tasks.PostPeriodicTask([&] { called++; }, kInterval, false);
loop.RunFor(kInterval * 4);
}
EXPECT_EQ(4, called);
called = 0;
loop.RunFor(kInterval * 4);
EXPECT_EQ(0, called);
}
// Verifies that all occurrences of a periodic task posted from a non-dispatch
// thread execute on the dispatch thread.
//
// Example bug:
// https://fuchsia.googlesource.com/garnet/+/3a8dab9a939efeeb5e18454595a558f9972f7a42/public/lib/callback/scoped_task_runner.cc#47
TEST(ScopedTaskRunnerTest, PostPeriodicTaskOffThread) {
async::TestLoop loop;
ScopedTaskRunner tasks(loop.dispatcher());
pthread_t dispatch_thread = pthread_self();
int called = 0;
std::thread foreign_thread([&] {
// test-correctness assertion
EXPECT_NE(dispatch_thread, pthread_self());
tasks.PostPeriodicTask(
[&] {
++called;
EXPECT_EQ(dispatch_thread, pthread_self()) << "iteration " << called;
},
kInterval, true);
});
foreign_thread.join();
loop.RunFor(kInterval * 4);
EXPECT_EQ(5, called);
}
} // namespace
} // namespace callback