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