implement lua object as Value
remove LuaJIT/Obj remove nf7::luajit::Obj add nf7::NodeRootLambda
This commit is contained in:
parent
cdf2760d2f
commit
cec3f79321
@ -79,7 +79,6 @@ target_sources(nf7
|
||||
common/logger_ref.hh
|
||||
common/luajit.hh
|
||||
common/luajit.cc
|
||||
common/luajit_obj.hh
|
||||
common/luajit_queue.hh
|
||||
common/luajit_ref.hh
|
||||
common/luajit_thread.hh
|
||||
@ -90,6 +89,7 @@ target_sources(nf7
|
||||
common/native_file.hh
|
||||
common/node.hh
|
||||
common/node_link_store.hh
|
||||
common/node_root_lambda.hh
|
||||
common/ptr_selector.hh
|
||||
common/queue.hh
|
||||
common/sequencer.hh
|
||||
@ -115,7 +115,6 @@ target_sources(nf7
|
||||
file/luajit_context.cc
|
||||
file/luajit_inline_node.cc
|
||||
file/luajit_node.cc
|
||||
file/luajit_obj.cc
|
||||
file/node_imm.cc
|
||||
file/node_network.cc
|
||||
file/node_ref.cc
|
||||
|
@ -59,14 +59,15 @@ void FileHolder::SetUp() noexcept {
|
||||
watcher_->AddHandler(nf7::File::Event::kRemove, [this](auto&) {
|
||||
file_ = nullptr;
|
||||
});
|
||||
if (mem) {
|
||||
watcher_->AddHandler(nf7::File::Event::kUpdate, [this, mem](auto&) {
|
||||
watcher_->AddHandler(nf7::File::Event::kUpdate, [this, mem](auto&) {
|
||||
if (mem) {
|
||||
auto ptag = std::exchange(tag_, mem->Save());
|
||||
if (ptag != tag_) {
|
||||
onChildMementoChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
onChildUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
// memento setup
|
||||
|
@ -103,7 +103,10 @@ class FileHolder : public nf7::FileBase::Feature {
|
||||
return own()? nf7::File::Path {{id_}}: std::get<nf7::File::Path>(entity_);
|
||||
}
|
||||
|
||||
// called when child's memento tag id is changed
|
||||
// called when kUpdate event is caused by the child
|
||||
std::function<void(void)> onChildUpdate = [](){};
|
||||
|
||||
// called when the child's memento tag id is changed
|
||||
std::function<void(void)> onChildMementoChange = [](){};
|
||||
|
||||
// called right before returning from Emplace()
|
||||
|
@ -240,8 +240,7 @@ class Future final {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
assert(imm_);
|
||||
ctx->env().ExecSub(ctx, std::bind(f, Future(*imm_)));
|
||||
ctx->env().ExecSub(ctx, std::bind(f, *this));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/future.hh"
|
||||
#include "common/luajit_queue.hh"
|
||||
#include "common/luajit_ref.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
class Obj : public nf7::File::Interface {
|
||||
public:
|
||||
Obj() = default;
|
||||
Obj(const Obj&) = delete;
|
||||
Obj(Obj&&) = delete;
|
||||
Obj& operator=(const Obj&) = delete;
|
||||
Obj& operator=(Obj&&) = delete;
|
||||
|
||||
// result is registered to LUA_REGISTRY
|
||||
virtual nf7::Future<std::shared_ptr<Ref>> Build() noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace nf7::luajit
|
@ -7,11 +7,12 @@
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/luajit_queue.hh"
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
class Ref final {
|
||||
class Ref final : public nf7::Value::Data {
|
||||
public:
|
||||
Ref() = delete;
|
||||
Ref(const std::shared_ptr<nf7::Context>& ctx,
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <tuple>
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
#include "common/luajit_obj.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
@ -19,11 +18,6 @@ constexpr size_t kBufferSizeMax = 64 * 1024 * 1024;
|
||||
// Pushes a metatable for Thread object, available on global table as 'nf7'.
|
||||
static void PushMeta(lua_State*) noexcept;
|
||||
|
||||
// Pushes Lua object built by a file who implements luajit::Obj interface.
|
||||
static void GetLuaObjAndPush(
|
||||
lua_State* L, const std::shared_ptr<Thread>& th, File& f);
|
||||
|
||||
|
||||
lua_State* Thread::Init(lua_State* L) noexcept {
|
||||
assert(state_ == kInitial);
|
||||
|
||||
@ -107,6 +101,17 @@ static void PushMeta(lua_State* L) noexcept {
|
||||
});
|
||||
lua_setfield(L, -2, "resolve");
|
||||
|
||||
// nf7:ref(obj)
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto th = Thread::GetPtr(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
|
||||
auto ref = std::make_shared<nf7::luajit::Ref>(th->ctx(), th->ljq(), L);
|
||||
PushValue(L, nf7::Value {std::move(ref)});
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "ref");
|
||||
|
||||
// nf7:query(file_id, interface)
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto th = Thread::GetPtr(L, 1);
|
||||
@ -121,7 +126,7 @@ static void PushMeta(lua_State* L) noexcept {
|
||||
} else if (iface == "exbuffer") {
|
||||
Thread::Lock<nf7::AsyncBuffer>::AcquireAndPush(L, th, f, true);
|
||||
} else if (iface == "lua") {
|
||||
GetLuaObjAndPush(L, th, f);
|
||||
// WIP GetLuaObjAndPush(L, th, f);
|
||||
} else if (iface == "node") {
|
||||
Thread::Lambda::CreateAndPush(L, th, f);
|
||||
} else {
|
||||
@ -185,23 +190,4 @@ static void PushMeta(lua_State* L) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
static void GetLuaObjAndPush(
|
||||
lua_State* L, const std::shared_ptr<Thread>& th, File& f) {
|
||||
f.interfaceOrThrow<nf7::luajit::Obj>().Build().
|
||||
Then([th, L](auto fu) {
|
||||
th->ljq()->Push(th->ctx(), [th, L, fu](auto) mutable {
|
||||
try {
|
||||
const auto& obj = fu.value();
|
||||
if (th->ljq() != obj->ljq()) {
|
||||
throw nf7::Exception {"the object is built on other LuaJIT context"};
|
||||
}
|
||||
obj->PushSelf(L);
|
||||
th->Resume(L, 1);
|
||||
} catch (nf7::Exception& e) {
|
||||
th->Resume(L, luajit::PushAll(L, nullptr, e.msg()));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace nf7::luajit
|
||||
|
119
common/node_root_lambda.hh
Normal file
119
common/node_root_lambda.hh
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/future.hh"
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class NodeRootLambda final : public nf7::Node::Lambda,
|
||||
public std::enable_shared_from_this<NodeRootLambda> {
|
||||
public:
|
||||
struct Builder;
|
||||
|
||||
NodeRootLambda(const NodeRootLambda&) = delete;
|
||||
NodeRootLambda(NodeRootLambda&&) = delete;
|
||||
NodeRootLambda& operator=(const NodeRootLambda&) = delete;
|
||||
NodeRootLambda& operator=(NodeRootLambda&&) = delete;
|
||||
|
||||
bool KeepAlive() noexcept {
|
||||
if (target_.expired() && pro_.size()) {
|
||||
for (auto& pro : pro_) {
|
||||
pro.second.Throw(std::make_exception_ptr(
|
||||
nf7::Exception {"output was never satisified"}));
|
||||
}
|
||||
}
|
||||
return !target_.expired();
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<nf7::Node::Lambda> target_;
|
||||
|
||||
std::unordered_map<std::string, nf7::Future<nf7::Value>::Promise> pro_;
|
||||
std::unordered_map<std::string, std::function<void(const nf7::Value&)>> handler_;
|
||||
|
||||
|
||||
using nf7::Node::Lambda::Lambda;
|
||||
void Handle(std::string_view name, const nf7::Value& v,
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override {
|
||||
const auto sname = std::string {name};
|
||||
auto pitr = pro_.find(sname);
|
||||
if (pitr != pro_.end()) {
|
||||
pitr->second.Return(nf7::Value {v});
|
||||
pro_.erase(pitr);
|
||||
}
|
||||
auto hitr = handler_.find(sname);
|
||||
if (hitr != handler_.end()) {
|
||||
hitr->second(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeRootLambda::Builder final {
|
||||
public:
|
||||
Builder() = delete;
|
||||
Builder(nf7::File& f, nf7::Node& n,
|
||||
const std::shared_ptr<nf7::Context>& ctx = nullptr) noexcept :
|
||||
prod_(new NodeRootLambda {f, ctx}), target_(n.CreateLambda(prod_)), node_(&n) {
|
||||
prod_->target_ = target_;
|
||||
}
|
||||
|
||||
void CheckOutput(std::string_view name) const {
|
||||
auto out = node_->GetOutputs();
|
||||
if (out.end() == std::find(out.begin(), out.end(), name)) {
|
||||
throw nf7::Exception {"required output is missing: "+std::string {name}};
|
||||
}
|
||||
}
|
||||
void CheckInput(std::string_view name) const {
|
||||
auto in = node_->GetInputs();
|
||||
if (in.end() == std::find(in.begin(), in.end(), name)) {
|
||||
throw nf7::Exception {"required input is missing: "+std::string {name}};
|
||||
}
|
||||
}
|
||||
|
||||
nf7::Future<nf7::Value> Receive(const std::string& name) {
|
||||
assert(!built_);
|
||||
CheckOutput(name);
|
||||
auto [itr, added] =
|
||||
prod_->pro_.try_emplace(name, nf7::Future<nf7::Value>::Promise {});
|
||||
assert(added);
|
||||
return itr->second.future();
|
||||
}
|
||||
void Listen(const std::string& name, std::function<void(const nf7::Value&)>&& f) {
|
||||
assert(!built_);
|
||||
CheckOutput(name);
|
||||
prod_->handler_[name] = std::move(f);
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeRootLambda> Build() noexcept {
|
||||
assert(!built_);
|
||||
built_ = true;
|
||||
return prod_;
|
||||
}
|
||||
|
||||
void Send(std::string_view name, const nf7::Value& v) noexcept {
|
||||
assert(built_);
|
||||
CheckInput(name);
|
||||
target_->Handle(name, v, prod_);
|
||||
}
|
||||
|
||||
private:
|
||||
bool built_ = false;
|
||||
|
||||
std::shared_ptr<NodeRootLambda> prod_;
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> target_;
|
||||
|
||||
nf7::Node* const node_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -17,6 +17,7 @@
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/file_holder.hh"
|
||||
#include "common/future.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
@ -24,14 +25,13 @@
|
||||
#include "common/gui_popup.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/logger_ref.hh"
|
||||
#include "common/luajit_obj.hh"
|
||||
#include "common/luajit_queue.hh"
|
||||
#include "common/luajit_ref.hh"
|
||||
#include "common/luajit_thread.hh"
|
||||
#include "common/memento.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/node_root_lambda.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/task.hh"
|
||||
#include "common/util_string.hh"
|
||||
|
||||
|
||||
@ -62,13 +62,11 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
|
||||
Node(Env& env, Data&& data = {}) noexcept :
|
||||
nf7::FileBase(kType, env, {&obj_, &obj_editor_, &socket_popup_}),
|
||||
nf7::DirItem(nf7::DirItem::kMenu |
|
||||
nf7::DirItem::kTooltip |
|
||||
nf7::DirItem::kWidget),
|
||||
nf7::DirItem(nf7::DirItem::kTooltip | nf7::DirItem::kWidget),
|
||||
life_(*this),
|
||||
log_(std::make_shared<nf7::LoggerRef>()),
|
||||
obj_(*this, "obj_factory"),
|
||||
obj_editor_(obj_, [](auto& t) { return t.flags().contains("nf7::luajit::Obj"); }),
|
||||
obj_editor_(obj_, [](auto& t) { return t.flags().contains("nf7::Node"); }),
|
||||
mem_(std::move(data)) {
|
||||
mem_.data().obj.SetTarget(obj_);
|
||||
mem_.CommitAmend();
|
||||
@ -83,6 +81,13 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
});
|
||||
};
|
||||
|
||||
obj_.onChildUpdate = [this]() {
|
||||
if (fu_) {
|
||||
log_->Info("factory update detected, dropping cache");
|
||||
}
|
||||
fu_ = std::nullopt;
|
||||
};
|
||||
|
||||
obj_.onChildMementoChange = [this]() { mem_.Commit(); };
|
||||
obj_.onEmplace = [this]() { mem_.Commit(); };
|
||||
|
||||
@ -113,7 +118,7 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
}
|
||||
|
||||
void Handle(const Event&) noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void Update() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
|
||||
@ -127,8 +132,6 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
nf7::Task<std::shared_ptr<nf7::luajit::Ref>>::Holder fetch_;
|
||||
|
||||
nf7::FileHolder obj_;
|
||||
nf7::gui::FileHolderEditor obj_editor_;
|
||||
|
||||
@ -138,26 +141,41 @@ class Node final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
const Data& data() const noexcept { return mem_.data(); }
|
||||
Data& data() noexcept { return mem_.data(); }
|
||||
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> FetchHandler() noexcept;
|
||||
// factory context
|
||||
std::shared_ptr<nf7::NodeRootLambda> factory_;
|
||||
std::optional<nf7::Future<nf7::Value>> fu_;
|
||||
};
|
||||
|
||||
class Node::Lambda final : public nf7::Node::Lambda,
|
||||
public std::enable_shared_from_this<Node::Lambda> {
|
||||
public:
|
||||
Lambda(Node& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
|
||||
nf7::Node::Lambda(f, parent), file_(f.life_), log_(f.log_) {
|
||||
nf7::Node::Lambda(f, parent), f_(f.life_), log_(f.log_) {
|
||||
}
|
||||
|
||||
void Handle(std::string_view name, const nf7::Value& v,
|
||||
void Handle(std::string_view k, const nf7::Value& v,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept override
|
||||
try {
|
||||
file_.EnforceAlive();
|
||||
|
||||
auto fu = file_->FetchHandler();
|
||||
f_.EnforceAlive();
|
||||
auto self = shared_from_this();
|
||||
fu.ThenSub(self, [self, name = std::string(name), v = v, caller](auto fu) mutable {
|
||||
self->CallHandler({std::move(name), std::move(v)}, fu, caller);
|
||||
|
||||
if (!f_->fu_) {
|
||||
auto& n = f_->obj_.GetFileOrThrow().interfaceOrThrow<nf7::Node>();
|
||||
auto b = nf7::NodeRootLambda::Builder {*f_, n};
|
||||
f_->fu_ = b.Receive("product");
|
||||
|
||||
f_->factory_ = b.Build();
|
||||
b.Send("create", nf7::Value::Pulse {});
|
||||
}
|
||||
|
||||
assert(f_->fu_);
|
||||
f_->fu_->ThenSub(self, [this, k = std::string {k}, v = v, caller](auto fu) mutable {
|
||||
try {
|
||||
auto ref = fu.value().template data<nf7::luajit::Ref>();
|
||||
CallFunc(ref, std::move(k), std::move(v), caller);
|
||||
} catch (nf7::Exception& e) {
|
||||
log_->Error("failed to call lua function: "+e.msg());
|
||||
}
|
||||
});
|
||||
} catch (nf7::LifeExpiredException&) {
|
||||
} catch (nf7::Exception& e) {
|
||||
@ -172,7 +190,7 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Node>::Ref file_;
|
||||
nf7::Life<Node>::Ref f_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
@ -181,35 +199,45 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
std::optional<nf7::luajit::Ref> ctxtable_;
|
||||
|
||||
|
||||
using Param = std::pair<std::string, nf7::Value>;
|
||||
void CallHandler(Param&& p, auto& fu, const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept
|
||||
try {
|
||||
void CallFunc(const std::shared_ptr<nf7::luajit::Ref>& func,
|
||||
std::string&& k, nf7::Value&& v,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& caller) {
|
||||
auto self = shared_from_this();
|
||||
th_.erase(
|
||||
std::remove_if(th_.begin(), th_.end(), [](auto& x) { return x.expired(); }),
|
||||
th_.end());
|
||||
|
||||
auto handler = fu.value();
|
||||
auto ljq = handler->ljq();
|
||||
|
||||
auto th = std::make_shared<nf7::luajit::Thread>(
|
||||
auto ljq = func->ljq();
|
||||
auto th = std::make_shared<nf7::luajit::Thread>(
|
||||
self, ljq, [self, ljq](auto& th, auto L) { self->HandleThread(ljq, th, L); });
|
||||
th->Install(log_);
|
||||
th_.emplace_back(th);
|
||||
|
||||
ljq->Push(self, [this, self, ljq, p = std::move(p), caller, handler, th](auto L) mutable {
|
||||
ljq->Push(self, [this, self, ljq, k = std::move(k), v = std::move(v), caller, func, th](auto L) mutable {
|
||||
auto thL = th->Init(L);
|
||||
handler->PushSelf(thL);
|
||||
lua_pushstring(thL, p.first.c_str());
|
||||
nf7::luajit::PushValue(thL, p.second);
|
||||
func->PushSelf(thL);
|
||||
|
||||
// push args
|
||||
lua_pushstring(thL, k.c_str());
|
||||
nf7::luajit::PushValue(thL, v);
|
||||
nf7::luajit::PushNodeLambda(thL, caller, self);
|
||||
PushContextTable(ljq, thL);
|
||||
|
||||
// push context table
|
||||
if (ctxtable_ && ctxtable_->ljq() != ljq) {
|
||||
ctxtable_ = std::nullopt;
|
||||
}
|
||||
if (!ctxtable_) {
|
||||
lua_createtable(thL, 0, 0);
|
||||
lua_pushvalue(thL, -1);
|
||||
ctxtable_.emplace(shared_from_this(), ljq, thL);
|
||||
} else {
|
||||
ctxtable_->PushSelf(thL);
|
||||
}
|
||||
|
||||
// execute
|
||||
th->Resume(thL, 4);
|
||||
});
|
||||
} catch (nf7::Exception& e) {
|
||||
log_->Error("failed to call handler: "+e.msg());
|
||||
}
|
||||
|
||||
void HandleThread(const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
||||
nf7::luajit::Thread& th, lua_State* L) noexcept {
|
||||
switch (th.state()) {
|
||||
@ -227,20 +255,6 @@ class Node::Lambda final : public nf7::Node::Lambda,
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PushContextTable(const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
||||
lua_State* L) noexcept {
|
||||
if (ctxtable_ && ctxtable_->ljq() != ljq) {
|
||||
ctxtable_ = std::nullopt;
|
||||
}
|
||||
if (!ctxtable_) {
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
ctxtable_.emplace(shared_from_this(), ljq, L);
|
||||
} else {
|
||||
ctxtable_->PushSelf(L);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -248,12 +262,6 @@ std::shared_ptr<nf7::Node::Lambda> Node::CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
|
||||
return std::make_shared<Lambda>(*this, parent);
|
||||
}
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Node::FetchHandler() noexcept
|
||||
try {
|
||||
return obj_.GetFileOrThrow().interfaceOrThrow<nf7::luajit::Obj>().Build();
|
||||
} catch (nf7::Exception&) {
|
||||
return {std::current_exception()};
|
||||
}
|
||||
|
||||
void Node::Handle(const Event& ev) noexcept {
|
||||
nf7::FileBase::Handle(ev);
|
||||
@ -271,9 +279,11 @@ void Node::Handle(const Event& ev) noexcept {
|
||||
return;
|
||||
}
|
||||
}
|
||||
void Node::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("fetch handler")) {
|
||||
FetchHandler();
|
||||
|
||||
void Node::Update() noexcept {
|
||||
nf7::FileBase::Update();
|
||||
if (factory_) {
|
||||
factory_->KeepAlive();
|
||||
}
|
||||
}
|
||||
void Node::UpdateTooltip() noexcept {
|
||||
|
@ -1,312 +0,0 @@
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <yas/serialize.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/file_base.hh"
|
||||
#include "common/file_holder.hh"
|
||||
#include "common/future.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_watcher.hh"
|
||||
#include "common/gui_file.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/lock.hh"
|
||||
#include "common/luajit.hh"
|
||||
#include "common/luajit_obj.hh"
|
||||
#include "common/luajit_queue.hh"
|
||||
#include "common/luajit_thread.hh"
|
||||
#include "common/logger_ref.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/task.hh"
|
||||
#include "common/yas_nf7.hh"
|
||||
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Obj final : public nf7::FileBase, public nf7::DirItem, public nf7::luajit::Obj {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Obj> kType = {
|
||||
"LuaJIT/Obj", {"nf7::DirItem", "nf7::luajit::Obj"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted(
|
||||
"Compiles and runs LuaJIT script, and caches the object returned from the script.");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted(
|
||||
"implements nf7::luajit::Obj implementation");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted(
|
||||
"requires nf7::luajit::Queue implementation with name '_luajit' on upper dir");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted(
|
||||
"requires nf7::AsyncBuffer implementation to load LuaJIT script");
|
||||
}
|
||||
|
||||
static constexpr size_t kMaxSize = 1024*1024*16; /* = 16 MiB */
|
||||
|
||||
class ExecTask;
|
||||
|
||||
struct Data final {
|
||||
nf7::FileHolder::Tag src;
|
||||
};
|
||||
|
||||
Obj(Env& env, Data&& data = {}) noexcept :
|
||||
nf7::FileBase(kType, env, {&src_, &src_editor_}),
|
||||
nf7::DirItem(nf7::DirItem::kTooltip |
|
||||
nf7::DirItem::kMenu |
|
||||
nf7::DirItem::kWidget),
|
||||
life_(*this),
|
||||
log_(std::make_shared<nf7::LoggerRef>()),
|
||||
src_(*this, "src"),
|
||||
src_editor_(src_, [](auto& t) { return t.flags().contains("nf7::AsyncBuffer"); }),
|
||||
mem_(std::move(data)) {
|
||||
mem_.data().src.SetTarget(src_);
|
||||
mem_.CommitAmend();
|
||||
|
||||
src_.onChildMementoChange = [this]() { mem_.Commit(); };
|
||||
src_.onEmplace = [this]() { mem_.Commit(); };
|
||||
|
||||
mem_.onRestore = [this]() { DropCache(); Touch(); };
|
||||
mem_.onCommit = [this]() { DropCache(); Touch(); };
|
||||
}
|
||||
|
||||
Obj(Env& env, Deserializer& ar) noexcept : Obj(env) {
|
||||
ar(src_);
|
||||
}
|
||||
void Serialize(Serializer& ar) const noexcept override {
|
||||
ar(src_);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<Obj>(env, Data {mem_.data()});
|
||||
}
|
||||
|
||||
void Handle(const Event&) noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Build() noexcept override;
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::DirItem, nf7::luajit::Obj, nf7::Memento>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Obj> life_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
std::optional<nf7::GenericWatcher> watcher_;
|
||||
std::shared_ptr<nf7::luajit::Ref> cache_;
|
||||
|
||||
nf7::Task<std::shared_ptr<nf7::luajit::Ref>>::Holder exec_;
|
||||
|
||||
nf7::FileHolder src_;
|
||||
nf7::gui::FileHolderEditor src_editor_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
|
||||
|
||||
void DropCache() noexcept;
|
||||
};
|
||||
|
||||
class Obj::ExecTask final : public nf7::Task<std::shared_ptr<nf7::luajit::Ref>> {
|
||||
public:
|
||||
ExecTask(Obj& target) noexcept :
|
||||
Task(target.env(), target.id()), target_(&target), log_(target.log_) {
|
||||
}
|
||||
|
||||
size_t GetMemoryUsage() const noexcept override {
|
||||
return buf_size_;
|
||||
}
|
||||
|
||||
private:
|
||||
Obj* const target_;
|
||||
|
||||
std::shared_ptr<nf7::LoggerRef> log_;
|
||||
|
||||
std::string chunkname_;
|
||||
std::atomic<size_t> buf_size_ = 0;
|
||||
std::vector<uint8_t> buf_;
|
||||
bool buf_consumed_ = false;
|
||||
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>>::Coro Proc() noexcept override {
|
||||
try {
|
||||
auto& srcf = target_->src_.GetFileOrThrow();
|
||||
chunkname_ = srcf.abspath().Stringify();
|
||||
|
||||
const auto srcf_id = srcf.id();
|
||||
|
||||
// acquire lock of source
|
||||
auto src = srcf.interfaceOrThrow<nf7::AsyncBuffer>().self();
|
||||
auto srclock = co_await src->AcquireLock(false);
|
||||
|
||||
// get size of source
|
||||
buf_size_ = co_await src->size();
|
||||
if (buf_size_ == 0) {
|
||||
throw nf7::Exception("source is empty");
|
||||
}
|
||||
if (buf_size_ > kMaxSize) {
|
||||
throw nf7::Exception("source is too huge");
|
||||
}
|
||||
|
||||
// read source
|
||||
buf_.resize(buf_size_);
|
||||
const size_t read = co_await src->Read(0, buf_.data(), buf_size_);
|
||||
if (read != buf_size_) {
|
||||
throw nf7::Exception("failed to read all bytes from source");
|
||||
}
|
||||
|
||||
// find luajit queue
|
||||
auto ljq = target_->
|
||||
ResolveUpwardOrThrow("_luajit").
|
||||
interfaceOrThrow<nf7::luajit::Queue>().self();
|
||||
|
||||
// create promise handler for new luajit thread
|
||||
nf7::Future<int>::Promise lua_pro(self());
|
||||
auto handler = nf7::luajit::Thread::CreatePromiseHandler<int>(
|
||||
lua_pro, [&](auto L) {
|
||||
if (lua_gettop(L) != 1) {
|
||||
throw nf7::Exception("expected one object to be returned");
|
||||
}
|
||||
return luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
});
|
||||
|
||||
// start watcher on target_->watcher_
|
||||
try {
|
||||
auto& w = target_->watcher_;
|
||||
w.emplace(env());
|
||||
w->Watch(srcf_id);
|
||||
|
||||
std::weak_ptr<Task> wself = self();
|
||||
w->AddHandler(Event::kUpdate, [t = target_, wself](auto&) {
|
||||
if (auto self = wself.lock()) {
|
||||
t->log_->Info("detected update of source file, aborts building");
|
||||
t->exec_ = {};
|
||||
} else if (t->cache_) {
|
||||
t->log_->Info("detected update of source file, drops cache automatically");
|
||||
t->cache_ = nullptr;
|
||||
t->Touch();
|
||||
}
|
||||
});
|
||||
} catch (Exception& e) {
|
||||
log_->Warn("watcher setup error: "+e.msg());
|
||||
}
|
||||
|
||||
// queue task to trigger the thread
|
||||
auto th = std::make_shared<nf7::luajit::Thread>(self(), ljq, std::move(handler));
|
||||
th->Install(log_);
|
||||
ljq->Push(self(), [&](auto L) {
|
||||
try {
|
||||
auto thL = th->Init(L);
|
||||
Compile(thL);
|
||||
th->Resume(thL, 0);
|
||||
} catch (Exception&) {
|
||||
lua_pro.Throw(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
// wait for end of execution and return built object's index
|
||||
const int idx = co_await lua_pro.future();
|
||||
|
||||
// context for object cache
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(env(), initiator(), "luajit object cache");
|
||||
|
||||
// return the object and cache it
|
||||
target_->cache_ = std::make_shared<nf7::luajit::Ref>(ctx, ljq, idx);
|
||||
co_yield target_->cache_;
|
||||
|
||||
} catch (Exception& e) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void Compile(lua_State* L) {
|
||||
static const auto kReader = [](lua_State*, void* selfptr, size_t* size) -> const char* {
|
||||
auto self = reinterpret_cast<ExecTask*>(selfptr);
|
||||
if (std::exchange(self->buf_consumed_, true)) {
|
||||
*size = 0;
|
||||
return nullptr;
|
||||
} else {
|
||||
*size = self->buf_.size();
|
||||
return reinterpret_cast<const char*>(self->buf_.data());
|
||||
}
|
||||
};
|
||||
if (0 != lua_load(L, kReader, this, chunkname_.c_str())) {
|
||||
throw nf7::Exception(lua_tostring(L, -1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
nf7::Future<std::shared_ptr<nf7::luajit::Ref>> Obj::Build() noexcept {
|
||||
if (auto exec = exec_.lock()) return exec->fu();
|
||||
if (cache_) {
|
||||
return std::shared_ptr<nf7::luajit::Ref>{cache_};
|
||||
}
|
||||
|
||||
auto exec = std::make_shared<ExecTask>(*this);
|
||||
exec->Start();
|
||||
exec_ = {exec};
|
||||
return exec->fu();
|
||||
}
|
||||
void Obj::Handle(const Event& ev) noexcept {
|
||||
nf7::FileBase::Handle(ev);
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
log_->SetUp(*this);
|
||||
break;
|
||||
case Event::kRemove:
|
||||
DropCache();
|
||||
log_->TearDown();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Obj::DropCache() noexcept {
|
||||
exec_ = {};
|
||||
cache_ = nullptr;
|
||||
watcher_ = std::nullopt;
|
||||
}
|
||||
|
||||
void Obj::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("build")) {
|
||||
Build();
|
||||
}
|
||||
if (ImGui::MenuItem("drop cache", nullptr, nullptr, !!cache_)) {
|
||||
DropCache();
|
||||
}
|
||||
}
|
||||
void Obj::UpdateTooltip() noexcept {
|
||||
ImGui::Text("cache: %s", cache_? "ready": "nothing");
|
||||
ImGui::Text("src :");
|
||||
ImGui::Indent();
|
||||
src_editor_.Tooltip();
|
||||
ImGui::Unindent();
|
||||
}
|
||||
void Obj::UpdateWidget() noexcept {
|
||||
ImGui::TextUnformatted("LuaJIT/Obj: config");
|
||||
src_editor_.ButtonWithLabel("src");
|
||||
ImGui::Spacing();
|
||||
src_editor_.ItemWidget("src");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
||||
|
@ -49,7 +49,7 @@ namespace {
|
||||
class Network final : public nf7::FileBase, public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const GenericTypeInfo<Network> kType = {
|
||||
"Node/Network", {"nf7::DirItem", ""}};
|
||||
"Node/Network", {"nf7::DirItem", "nf7::Node"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted("A Node composed of multiple child Nodes, whose sockets are linked to each other");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::Node");
|
||||
|
Loading…
x
Reference in New Issue
Block a user