add luajit::Context

This commit is contained in:
falsycat 2023-07-30 00:03:47 +09:00
parent 3caa4f5ab7
commit 9aafa6e0a8
5 changed files with 352 additions and 1 deletions

View File

@ -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
View 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
View 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

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