add uv::Concurrency
This commit is contained in:
parent
a7411c1154
commit
ad7683fd44
@ -12,11 +12,13 @@ target_sources(nf7_core
|
|||||||
luajit/context.cc
|
luajit/context.cc
|
||||||
luajit/lambda.cc
|
luajit/lambda.cc
|
||||||
luajit/thread.cc
|
luajit/thread.cc
|
||||||
|
uv/concurrency.cc
|
||||||
version.cc
|
version.cc
|
||||||
PUBLIC
|
PUBLIC
|
||||||
luajit/context.hh
|
luajit/context.hh
|
||||||
luajit/lambda.hh
|
luajit/lambda.hh
|
||||||
luajit/thread.hh
|
luajit/thread.hh
|
||||||
|
uv/concurrency.hh
|
||||||
uv/context.hh
|
uv/context.hh
|
||||||
uv/clock.hh
|
uv/clock.hh
|
||||||
clock.hh
|
clock.hh
|
||||||
@ -33,6 +35,7 @@ target_sources(nf7_core_test
|
|||||||
luajit/thread_test.cc
|
luajit/thread_test.cc
|
||||||
luajit/thread_test.hh
|
luajit/thread_test.hh
|
||||||
uv/context_test.hh
|
uv/context_test.hh
|
||||||
|
uv/concurrency_test.cc
|
||||||
clock_test.cc
|
clock_test.cc
|
||||||
)
|
)
|
||||||
target_link_libraries(nf7_core_test
|
target_link_libraries(nf7_core_test
|
||||||
|
79
core/uv/concurrency.cc
Normal file
79
core/uv/concurrency.cc
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/uv/concurrency.hh"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace nf7::core::uv {
|
||||||
|
|
||||||
|
Concurrency::Concurrency(Env& env, const std::shared_ptr<Context>& ctx)
|
||||||
|
try : subsys::Concurrency("nf7::core::uv::Concurrency"),
|
||||||
|
impl_(std::make_shared<Impl>(env)),
|
||||||
|
delete_(ctx->Make<uvw::async_handle>()),
|
||||||
|
push_(ctx->Make<uvw::async_handle>()),
|
||||||
|
timer_(ctx->Make<uvw::timer_handle>()) {
|
||||||
|
delete_->unreference();
|
||||||
|
push_->unreference();
|
||||||
|
timer_->unreference();
|
||||||
|
|
||||||
|
delete_->on<uvw::async_event>([p = push_, t = timer_](auto&, auto& self) {
|
||||||
|
p->close();
|
||||||
|
t->close();
|
||||||
|
self.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto consume = [impl = impl_, timer = timer_](auto&, auto& handle) {
|
||||||
|
handle.unreference();
|
||||||
|
const auto wait = impl->Consume();
|
||||||
|
const auto wake = timer->due_in();
|
||||||
|
if (0ms < wait && (wake == 0ms || wait < wake)) {
|
||||||
|
timer->reference();
|
||||||
|
timer->start(wait, 0ms);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
push_->on<uvw::async_event>(consume);
|
||||||
|
timer_->on<uvw::timer_event>(consume);
|
||||||
|
} catch(const std::bad_alloc&) {
|
||||||
|
throw Exception {"memory shortage"};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Concurrency::Push(SyncTask&& task) noexcept {
|
||||||
|
impl_->Push(std::move(task));
|
||||||
|
push_->reference();
|
||||||
|
push_->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
Concurrency::Impl::Impl(Env& env)
|
||||||
|
: clock_(env.Get<subsys::Clock>()),
|
||||||
|
logger_(env.GetOr<subsys::Logger>(NullLogger::instance())) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::milliseconds Concurrency::Impl::Consume() noexcept {
|
||||||
|
for (;;) {
|
||||||
|
const auto now = clock_->now();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> k {mtx_};
|
||||||
|
if (tasks_.empty()) {
|
||||||
|
return std::chrono::milliseconds {0};
|
||||||
|
}
|
||||||
|
const auto& top = tasks_.top();
|
||||||
|
if (top.after() > now) {
|
||||||
|
return std::chrono::duration_cast<
|
||||||
|
std::chrono::milliseconds>(top.after() - now);
|
||||||
|
}
|
||||||
|
auto task = top;
|
||||||
|
tasks_.pop();
|
||||||
|
k.unlock();
|
||||||
|
|
||||||
|
SyncTaskContext ctx {};
|
||||||
|
try {
|
||||||
|
task(ctx);
|
||||||
|
} catch (const Exception&) {
|
||||||
|
logger_->Error("task threw an exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nf7::core::uv
|
67
core/uv/concurrency.hh
Normal file
67
core/uv/concurrency.hh
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <uvw.hpp>
|
||||||
|
|
||||||
|
#include "iface/common/task.hh"
|
||||||
|
#include "iface/subsys/clock.hh"
|
||||||
|
#include "iface/subsys/concurrency.hh"
|
||||||
|
#include "iface/subsys/logger.hh"
|
||||||
|
#include "iface/env.hh"
|
||||||
|
|
||||||
|
#include "core/uv/context.hh"
|
||||||
|
#include "core/logger.hh"
|
||||||
|
|
||||||
|
namespace nf7::core::uv {
|
||||||
|
|
||||||
|
class Concurrency : public subsys::Concurrency {
|
||||||
|
public:
|
||||||
|
explicit Concurrency(Env& env) : Concurrency(env, env.Get<Context>()) { }
|
||||||
|
Concurrency(Env&, const std::shared_ptr<Context>&);
|
||||||
|
~Concurrency() noexcept override { delete_->send(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// THREAD-SAFE
|
||||||
|
void Push(SyncTask&& task) noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl {
|
||||||
|
public:
|
||||||
|
explicit Impl(Env&);
|
||||||
|
|
||||||
|
void Push(SyncTask&& task) noexcept
|
||||||
|
try {
|
||||||
|
std::unique_lock<std::mutex> k {mtx_};
|
||||||
|
tasks_.push(std::move(task));
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
logger_->Error("a task might be dismissed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns duration to wait or 0
|
||||||
|
std::chrono::milliseconds Consume() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::shared_ptr<subsys::Clock> clock_;
|
||||||
|
const std::shared_ptr<subsys::Logger> logger_;
|
||||||
|
|
||||||
|
std::mutex mtx_;
|
||||||
|
std::priority_queue<
|
||||||
|
SyncTask, std::vector<SyncTask>, std::greater<SyncTask>> tasks_;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::shared_ptr<Impl> impl_;
|
||||||
|
|
||||||
|
const std::shared_ptr<uvw::async_handle> delete_;
|
||||||
|
const std::shared_ptr<uvw::async_handle> push_;
|
||||||
|
const std::shared_ptr<uvw::timer_handle> timer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nf7::core::uv
|
52
core/uv/concurrency_test.cc
Normal file
52
core/uv/concurrency_test.cc
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/uv/concurrency.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "iface/subsys/clock.hh"
|
||||||
|
|
||||||
|
#include "core/uv/context_test.hh"
|
||||||
|
#include "core/clock.hh"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
using UV_Concurrency = nf7::core::uv::test::ContextFixture;
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(UV_Concurrency, Push) {
|
||||||
|
auto sut = std::make_shared<nf7::core::uv::Concurrency>(*env_);
|
||||||
|
|
||||||
|
auto called = uint64_t {0};
|
||||||
|
sut->Exec([&](auto&) { ++called; });
|
||||||
|
|
||||||
|
ctx_->Run();
|
||||||
|
EXPECT_EQ(called, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UV_Concurrency, PushFromTask) {
|
||||||
|
auto sut = std::make_shared<nf7::core::uv::Concurrency>(*env_);
|
||||||
|
|
||||||
|
auto called = uint64_t {0};
|
||||||
|
sut->Exec([&](auto&) { sut->Exec([&](auto&) { ++called; }); });
|
||||||
|
|
||||||
|
ctx_->Run();
|
||||||
|
EXPECT_EQ(called, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UV_Concurrency, PushWithDelay) {
|
||||||
|
auto clock = env_->Get<nf7::subsys::Clock>();
|
||||||
|
auto sut = std::make_shared<nf7::core::uv::Concurrency>(*env_);
|
||||||
|
|
||||||
|
auto called = uint64_t {0};
|
||||||
|
sut->Push({clock->now() + 100ms, [&](auto&) { ++called; }});
|
||||||
|
|
||||||
|
const auto begin = clock->now();
|
||||||
|
ctx_->Run();
|
||||||
|
const auto end = clock->now();
|
||||||
|
|
||||||
|
EXPECT_EQ(called, 1);
|
||||||
|
EXPECT_GE(end-begin, 100ms);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user