| // 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(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 |