| // Copyright 2020 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 <lib/fit/bridge.h> | |
| #include <lib/fit/promise.h> | |
| #include <lib/synchronous-executor/executor.h> | |
| #include <atomic> | |
| #include <memory> | |
| #include <thread> | |
| #include <fbl/auto_call.h> | |
| #include <zxtest/zxtest.h> | |
| namespace synchronous_executor { | |
| TEST(SynchronousExecutorTests, OnlyRunRunnableTasks) { | |
| synchronous_executor executor; | |
| int run_count = 0; | |
| fit::suspended_task task_handle; | |
| executor.schedule_task(fit::make_promise([&run_count, &task_handle](fit::context& context) { | |
| run_count++; | |
| task_handle = context.suspend_task(); | |
| return fit::pending(); | |
| })); | |
| executor.run_until_idle(); | |
| executor.run_until_idle(); | |
| ASSERT_EQ(run_count, 1); | |
| task_handle.resume_task(); | |
| executor.run_until_idle(); | |
| ASSERT_EQ(run_count, 2); | |
| } | |
| TEST(SynchronousExecutorTests, SuspendResumeTest) { | |
| synchronous_executor executor; | |
| int run_count = 0; | |
| fit::suspended_task task_handle; | |
| executor.schedule_task(fit::make_promise([&run_count, &task_handle](fit::context& context) { | |
| run_count++; | |
| task_handle = context.suspend_task(); | |
| return fit::pending(); | |
| })); | |
| executor.run_until_idle(); | |
| ASSERT_EQ(run_count, 1); | |
| task_handle.resume_task(); | |
| executor.run_until_idle(); | |
| ASSERT_EQ(run_count, 2); | |
| } | |
| TEST(SynchronousExecutorTests, ExecutorIsReentrantSafe) { | |
| synchronous_executor executor; | |
| int run_count = 0; | |
| bool reentered = false; | |
| fit::suspended_task task_handle; | |
| executor.schedule_task( | |
| fit::make_promise([&run_count, &executor, &reentered](fit::context& context) { | |
| run_count++; | |
| bool set_var = false; | |
| executor.schedule_task(fit::make_promise([&set_var]() { set_var = true; })); | |
| EXPECT_FALSE(set_var); | |
| executor.run_until_idle(); | |
| reentered = set_var; | |
| return fit::ok(); | |
| })); | |
| executor.run_until_idle(); | |
| ASSERT_EQ(run_count, 1); | |
| ASSERT_TRUE(reentered); | |
| } | |
| TEST(SynchronousExecutorTests, ExecutorIsThreadSafe) { | |
| synchronous_executor executor; | |
| std::atomic_size_t run_count = 0; | |
| std::thread thread([&]() { | |
| for (size_t i = 0; i < 1000; i++) { | |
| executor.schedule_task(fit::make_promise([&run_count](fit::context& context) { | |
| run_count++; | |
| return fit::ok(); | |
| })); | |
| executor.run_until_idle(); | |
| } | |
| }); | |
| for (size_t i = 0; i < 1000; i++) { | |
| executor.schedule_task(fit::make_promise([&run_count](fit::context& context) { | |
| run_count++; | |
| return fit::ok(); | |
| })); | |
| executor.run_until_idle(); | |
| } | |
| thread.join(); | |
| ASSERT_EQ(run_count.load(), 2000); | |
| } | |
| TEST(SynchronousExecutorTests, AbandonedTasksGetProperlyCleanedUp) { | |
| synchronous_executor executor; | |
| int run_count = 0; | |
| fit::suspended_task task_handle; | |
| int cleanup_count = 0; | |
| class AutoCleanup { | |
| public: | |
| AutoCleanup(int* counter) : counter_(counter) {} | |
| ~AutoCleanup() { (*counter_)++; } | |
| private: | |
| int* counter_; | |
| }; | |
| auto cleanup = std::make_unique<AutoCleanup>(&cleanup_count); | |
| executor.schedule_task(fit::make_promise( | |
| [&run_count, &task_handle, cleaner = std::move(cleanup)](fit::context& context) { | |
| run_count++; | |
| task_handle = context.suspend_task(); | |
| return fit::pending(); | |
| })); | |
| executor.run_until_idle(); | |
| ASSERT_EQ(cleanup_count, 0); | |
| ASSERT_EQ(run_count, 1); | |
| task_handle.reset(); | |
| ASSERT_EQ(run_count, 1); | |
| ASSERT_EQ(cleanup_count, 1); | |
| } | |
| } // namespace synchronous_executor |