add luajit::Context
This commit is contained in:
parent
3caa4f5ab7
commit
9aafa6e0a8
@ -1,13 +1,31 @@
|
||||
add_library(nf7_core)
|
||||
target_link_libraries(nf7_core
|
||||
PRIVATE
|
||||
PUBLIC
|
||||
git_hash
|
||||
luajit
|
||||
nf7_config
|
||||
nf7_iface
|
||||
)
|
||||
target_sources(nf7_core
|
||||
PRIVATE
|
||||
luajit/context.cc
|
||||
version.cc
|
||||
PUBLIC
|
||||
luajit/context.hh
|
||||
version.hh
|
||||
)
|
||||
|
||||
add_executable(nf7_core_test)
|
||||
target_sources(nf7_core_test
|
||||
PRIVATE
|
||||
luajit/context_test.cc
|
||||
luajit/context_test.hh
|
||||
)
|
||||
target_link_libraries(nf7_core_test
|
||||
PRIVATE
|
||||
nf7_core
|
||||
GTest::gmock_main
|
||||
GTest::gtest_main
|
||||
)
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(nf7_core_test)
|
||||
|
67
core/luajit/context.cc
Normal file
67
core/luajit/context.cc
Normal file
@ -0,0 +1,67 @@
|
||||
// No copyright
|
||||
#include "core/luajit/context.hh"
|
||||
|
||||
#include "iface/subsys/concurrency.hh"
|
||||
#include "iface/subsys/parallelism.hh"
|
||||
|
||||
|
||||
namespace nf7::core::luajit {
|
||||
|
||||
Value::~Value() noexcept {
|
||||
ctx_->Push(Task {[index = index_](auto& ctx) {
|
||||
luaL_unref(*ctx, LUA_REGISTRYINDEX, index);
|
||||
}});
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Value> TaskContext::Register() noexcept {
|
||||
const auto index = luaL_ref(state_, LUA_REGISTRYINDEX);
|
||||
return std::make_shared<Value>(ctx_, index);
|
||||
}
|
||||
|
||||
void TaskContext::Query(const std::shared_ptr<Value>& value) noexcept {
|
||||
assert(nullptr != value);
|
||||
assert(value->context() == ctx_);
|
||||
lua_rawgeti(state_, LUA_REGISTRYINDEX, value->index());
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
class ContextImpl final : public Context {
|
||||
public:
|
||||
ContextImpl(const char* name, Kind kind, Env& env)
|
||||
: Context(name, kind), tasq_(env.Get<T>()) { }
|
||||
|
||||
void Push(Task&& task) noexcept override {
|
||||
auto self = std::dynamic_pointer_cast<ContextImpl<T>>(shared_from_this());
|
||||
tasq_->Push(typename T::Item {
|
||||
[self, task = std::move(task)](auto&) mutable {
|
||||
TaskContext ctx {self, self->state()};
|
||||
lua_settop(*ctx, 0);
|
||||
task(ctx);
|
||||
},
|
||||
task.location()
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
using Context::shared_from_this;
|
||||
|
||||
private:
|
||||
std::shared_ptr<T> tasq_;
|
||||
};
|
||||
|
||||
std::shared_ptr<Context> Context::Create(Env& env, Kind kind) {
|
||||
switch (kind) {
|
||||
case kSync:
|
||||
return std::make_shared<ContextImpl<subsys::Concurrency>>(
|
||||
"nf7::core::luajit::SyncContext", kSync, env);
|
||||
case kAsync:
|
||||
return std::make_shared<ContextImpl<subsys::Parallelism>>(
|
||||
"nf7::core::luajit::AsyncContext", kAsync, env);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nf7::core::luajit
|
113
core/luajit/context.hh
Normal file
113
core/luajit/context.hh
Normal file
@ -0,0 +1,113 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
#include "iface/common/task.hh"
|
||||
#include "iface/subsys/interface.hh"
|
||||
#include "iface/env.hh"
|
||||
|
||||
|
||||
namespace nf7::core::luajit {
|
||||
|
||||
class Value;
|
||||
class TaskContext;
|
||||
class Context;
|
||||
|
||||
using Task = nf7::Task<TaskContext&>;
|
||||
using TaskQueue = nf7::TaskQueue<Task>;
|
||||
|
||||
class Value final {
|
||||
public:
|
||||
Value() = delete;
|
||||
Value(const std::shared_ptr<Context>& ctx, int index) noexcept
|
||||
: ctx_(ctx), index_(index) {
|
||||
assert(nullptr != ctx_);
|
||||
}
|
||||
~Value() noexcept;
|
||||
|
||||
Value(const Value&) = delete;
|
||||
Value(Value&&) = delete;
|
||||
Value& operator=(const Value&) = delete;
|
||||
Value& operator=(Value&&) = delete;
|
||||
|
||||
const std::shared_ptr<Context>& context() const noexcept { return ctx_; }
|
||||
int index() const noexcept { return index_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Context> ctx_;
|
||||
int index_;
|
||||
};
|
||||
|
||||
class TaskContext final {
|
||||
public:
|
||||
friend class Context;
|
||||
|
||||
TaskContext() = delete;
|
||||
explicit TaskContext(
|
||||
std::shared_ptr<Context>&& ctx, lua_State* state) noexcept
|
||||
: ctx_(std::move(ctx)), state_(state) {
|
||||
assert(nullptr != state_);
|
||||
}
|
||||
|
||||
TaskContext(const TaskContext&) = delete;
|
||||
TaskContext(TaskContext&&) = delete;
|
||||
TaskContext& operator=(const TaskContext&) = delete;
|
||||
TaskContext& operator=(TaskContext&&) = delete;
|
||||
|
||||
lua_State* operator*() const noexcept { return state_; }
|
||||
|
||||
std::shared_ptr<Value> Register() noexcept;
|
||||
void Query(const std::shared_ptr<Value>&) noexcept;
|
||||
|
||||
const std::shared_ptr<Context>& context() const noexcept { return ctx_; }
|
||||
lua_State* state() const noexcept { return state_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Context> ctx_;
|
||||
lua_State* state_;
|
||||
};
|
||||
|
||||
class Context :
|
||||
public subsys::Interface,
|
||||
public TaskQueue {
|
||||
public:
|
||||
using Item = Task;
|
||||
|
||||
enum Kind {
|
||||
kSync,
|
||||
kAsync,
|
||||
};
|
||||
static std::shared_ptr<Context> Create(Env&, Kind);
|
||||
|
||||
explicit Context(const char* name, Kind kind)
|
||||
: subsys::Interface(name), kind_(kind), state_(nullptr) {
|
||||
state_ = luaL_newstate();
|
||||
if (nullptr == state_) {
|
||||
throw Exception {"lua_State allocation failure"};
|
||||
}
|
||||
}
|
||||
~Context() noexcept {
|
||||
lua_close(state_);
|
||||
}
|
||||
|
||||
using TaskQueue::Push;
|
||||
using TaskQueue::Wrap;
|
||||
using TaskQueue::Exec;
|
||||
using TaskQueue::ExecAnd;
|
||||
|
||||
Kind kind() const noexcept { return kind_; }
|
||||
|
||||
protected:
|
||||
lua_State* state() const noexcept { return state_; }
|
||||
|
||||
private:
|
||||
Kind kind_;
|
||||
lua_State* state_;
|
||||
};
|
||||
|
||||
} // namespace nf7::core::luajit
|
50
core/luajit/context_test.cc
Normal file
50
core/luajit/context_test.cc
Normal file
@ -0,0 +1,50 @@
|
||||
// No copyright
|
||||
#include "core/luajit/context.hh"
|
||||
#include "core/luajit/context_test.hh"
|
||||
|
||||
|
||||
using LuaJIT_Context = nf7::core::luajit::test::ContextFixture;
|
||||
using LuaJIT_Value = nf7::core::luajit::test::ContextFixture;
|
||||
|
||||
TEST_P(LuaJIT_Context, CreateAndDestroy) {
|
||||
auto sut = nf7::core::luajit::Context::Create(*env_, GetParam());
|
||||
EXPECT_EQ(sut->kind(), GetParam());
|
||||
}
|
||||
TEST_P(LuaJIT_Context, Register) {
|
||||
auto sut = nf7::core::luajit::Context::Create(*env_, GetParam());
|
||||
sut->Exec([](auto& ctx) {
|
||||
lua_createtable(*ctx, 0, 0);
|
||||
auto value = ctx.Register();
|
||||
|
||||
EXPECT_NE(value->index(), LUA_REFNIL);
|
||||
EXPECT_NE(value->index(), LUA_NOREF);
|
||||
});
|
||||
|
||||
ConsumeTasks();
|
||||
}
|
||||
TEST_P(LuaJIT_Context, Query) {
|
||||
auto sut = nf7::core::luajit::Context::Create(*env_, GetParam());
|
||||
|
||||
std::shared_ptr<nf7::core::luajit::Value> value;
|
||||
|
||||
sut->Exec([&](auto& ctx) {
|
||||
lua_pushstring(*ctx, "helloworld");
|
||||
value = ctx.Register();
|
||||
});
|
||||
sut->Exec([&](auto& ctx) {
|
||||
EXPECT_TRUE(nullptr != value);
|
||||
|
||||
ctx.Query(value);
|
||||
const char* value = lua_tostring(*ctx, -1);
|
||||
|
||||
EXPECT_STREQ(value, "helloworld");
|
||||
});
|
||||
|
||||
ConsumeTasks();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SyncOrAsync, LuaJIT_Context,
|
||||
testing::Values(
|
||||
nf7::core::luajit::Context::kSync,
|
||||
nf7::core::luajit::Context::kAsync));
|
103
core/luajit/context_test.hh
Normal file
103
core/luajit/context_test.hh
Normal file
@ -0,0 +1,103 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include "core/luajit/context.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "iface/common/exception.hh"
|
||||
#include "iface/common/task.hh"
|
||||
#include "iface/subsys/concurrency.hh"
|
||||
#include "iface/subsys/parallelism.hh"
|
||||
#include "iface/env.hh"
|
||||
|
||||
namespace nf7::core::luajit::test {
|
||||
|
||||
class ContextFixture : public ::testing::TestWithParam<Context::Kind> {
|
||||
private:
|
||||
template <TaskLike T>
|
||||
class Driver final {
|
||||
public:
|
||||
using Param = typename T::Param;
|
||||
using Time = typename T::Time;
|
||||
|
||||
explicit Driver(Param p) : param_(std::forward<Param>(p)) { }
|
||||
|
||||
Driver(const Driver&) = delete;
|
||||
Driver(Driver&&) = delete;
|
||||
Driver& operator=(const Driver&) = delete;
|
||||
Driver& operator=(Driver&&) = delete;
|
||||
|
||||
void BeginBusy() noexcept { }
|
||||
void EndBusy() noexcept { interrupt_ = true; }
|
||||
void Drive(T&& task) noexcept {
|
||||
try {
|
||||
task(param_);
|
||||
} catch (const Exception& e) {
|
||||
std::cout
|
||||
<< "unexpected exception while task execution: " << e.what()
|
||||
<< std::endl;
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
Time tick() const noexcept {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
return std::chrono::time_point_cast<typename Time::duration>(now);
|
||||
}
|
||||
bool nextIdleInterruption() const noexcept { return interrupt_; }
|
||||
bool nextTaskInterruption() const noexcept { return false; }
|
||||
|
||||
private:
|
||||
bool interrupt_ = false;
|
||||
Param param_;
|
||||
};
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
syncq_ = std::make_shared<SimpleTaskQueue<SyncTask>>();
|
||||
asyncq_ = std::make_shared<SimpleTaskQueue<AsyncTask>>();
|
||||
env_.emplace(SimpleEnv::FactoryMap {
|
||||
{
|
||||
typeid(subsys::Concurrency), [this](auto&) {
|
||||
return std::make_shared<
|
||||
WrappedTaskQueue<subsys::Concurrency>>(syncq_);
|
||||
},
|
||||
},
|
||||
{
|
||||
typeid(subsys::Parallelism), [this](auto&) {
|
||||
return std::make_shared<
|
||||
WrappedTaskQueue<subsys::Parallelism>>(asyncq_);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
void TearDown() override {
|
||||
ConsumeTasks();
|
||||
env_ = std::nullopt;
|
||||
asyncq_ = nullptr;
|
||||
syncq_ = nullptr;
|
||||
}
|
||||
|
||||
void ConsumeTasks() noexcept {
|
||||
AsyncTaskContext async_ctx;
|
||||
Driver<AsyncTask> async_driver {async_ctx};
|
||||
asyncq_->Drive(async_driver);
|
||||
|
||||
SyncTaskContext sync_ctx;
|
||||
Driver<SyncTask> sync_driver {sync_ctx};
|
||||
syncq_->Drive(sync_driver);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<SimpleTaskQueue<SyncTask>> syncq_;
|
||||
std::shared_ptr<SimpleTaskQueue<AsyncTask>> asyncq_;
|
||||
std::optional<SimpleEnv> env_;
|
||||
};
|
||||
|
||||
} // namespace nf7::core::luajit::test
|
Loading…
x
Reference in New Issue
Block a user