add Task and TaskQueue

This commit is contained in:
falsycat 2023-07-17 21:37:49 +09:00
parent 07209204b9
commit 63911d448d
4 changed files with 173 additions and 0 deletions

View File

@ -13,6 +13,7 @@ target_sources(nf7_iface
common/container.hh
common/future.hh
common/observer.hh
common/task.hh
common/value.hh
version.hh
)
@ -25,6 +26,7 @@ target_sources(nf7_iface_test
common/future_test.cc
common/observer_test.hh
common/observer_test.cc
common/task_test.cc
common/value_test.cc
)
target_link_libraries(nf7_iface_test

106
iface/common/task.hh Normal file
View File

@ -0,0 +1,106 @@
// No copyright
#pragma once
#include <cassert>
#include <functional>
#include <memory>
#include <optional>
#include <source_location>
#include <string>
#include <string_view>
#include <utility>
#include "iface/common/exception.hh"
#include "iface/common/future.hh"
namespace nf7 {
class Task final {
public:
Task() = delete;
explicit Task(
std::function<void()>&& func,
std::source_location location = std::source_location::current()) noexcept
: func_(std::move(func)),
location_(location) {
assert(func_);
}
Task(const Task&) = delete;
Task(Task&&) = default;
Task& operator=(const Task&) = delete;
Task& operator=(Task&&) = default;
void Run() {
if (!func_) {
throw Exception {"double run is not allowed", location_};
}
try {
auto f = std::move(func_);
f();
} catch (...) {
throw Exception {"task throws an exception", location_};
}
}
private:
std::function<void()> func_;
std::source_location location_;
};
class TaskQueue : public std::enable_shared_from_this<TaskQueue> {
public:
TaskQueue() = default;
virtual ~TaskQueue() = default;
TaskQueue(const TaskQueue&) = delete;
TaskQueue(TaskQueue&&) = delete;
TaskQueue& operator=(const TaskQueue&) = delete;
TaskQueue& operator=(TaskQueue&&) = delete;
// THREAD SAFE
// an implementation must handle memory errors well
virtual void Push(Task&&) noexcept = 0;
// THREAD SAFE
auto Wrap(
auto&& f,
std::source_location loc = std::source_location::current()) noexcept {
return [self = shared_from_this(), f = std::move(f), loc](auto&&... args) {
self->Push(Task {[f = std::move(f),
...args = std::forward<decltype(args)>(args)]() {
f(std::forward<decltype(args)>(args)...);
}, loc});
};
}
// THREAD SAFE
template <typename R>
Future<R> RunAnd(
std::function<R()>&& f,
std::source_location loc = std::source_location::current()) noexcept {
return RunAnd({}, std::move(f));
}
// THREAD SAFE
template <typename R>
Future<R> RunAnd(
Future<R>::Completer&& comp,
std::function<R()>&& f,
std::source_location loc = std::source_location::current()) noexcept {
Future<R> future {comp};
Push(Task {
[f = std::move(f), comp = std::move(comp)]() { comp.Run(f); }, loc});
return future;
}
// THREAD SAFE
void Run(
std::function<void()>&& f,
std::source_location loc = std::source_location::current()) noexcept {
Push(Task {std::move(f), loc});
}
};
} // namespace nf7

47
iface/common/task_test.cc Normal file
View File

@ -0,0 +1,47 @@
// No copyright
#include "iface/common/task.hh"
#include "iface/common/task_test.hh"
#include <gtest/gtest.h>
#include "iface/common/future.hh"
TEST(Task, RunAndThrow) {
const auto line = __LINE__ + 1;
nf7::Task task {[&]() { throw nf7::Exception {"hello"}; }};
try {
task.Run();
EXPECT_FALSE("unreachable (exception expected)");
} catch (const nf7::Exception& e) {
EXPECT_EQ(e.location().line(), line);
EXPECT_EQ(e.location().file_name(), __FILE__);
}
}
TEST(TaskQueue, Wrap) {
auto sut = std::make_shared<nf7::test::TaskQueueMock>();
auto wrapped = sut->Wrap([](){});
EXPECT_CALL(*sut, Push(::testing::_)).Times(1);
wrapped();
}
TEST(TaskQueue, WrapInFutureThen) {
auto sut = std::make_shared<nf7::test::TaskQueueMock>();
ON_CALL(*sut, Push(::testing::_)).WillByDefault([](auto&& task) {
task.Run();
});
nf7::Future<int32_t> fut {int32_t {777}};
auto called = uint32_t {0};
fut.Then(sut->Wrap([&](auto& x) {
++called;
EXPECT_EQ(x, int32_t {777});
}));
EXPECT_EQ(called, 1);
}

18
iface/common/task_test.hh Normal file
View File

@ -0,0 +1,18 @@
// No copyright
#pragma once
#include "iface/common/task.hh"
#include <gmock/gmock.h>
namespace nf7::test {
class TaskQueueMock : public nf7::TaskQueue {
public:
TaskQueueMock() = default;
MOCK_METHOD(void, Push, (Task&&), (noexcept));
};
} // namespace nf7::test