blob: c23542e408e3ef7155939b150db1bf89542f5130 [file] [log] [blame]
// 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