From 3c67497229917a29521ce4c8c481e6366e9d7fa9 Mon Sep 17 00:00:00 2001 From: falsycat Date: Tue, 15 Nov 2022 15:02:07 +0900 Subject: [PATCH] fix an issue that active LuaJIT thread prevents Nf7 from shutting down --- CMakeLists.txt | 1 + common/context_owner.hh | 50 +++++++++++++++++++++++++++++++++ common/luajit_nfile_importer.hh | 9 +++--- common/luajit_thread.cc | 16 +++++------ common/luajit_thread.hh | 24 ++++++++++------ common/node_root_lambda.hh | 8 ++++++ file/luajit_node.cc | 12 ++++++-- file/node_singleton.cc | 13 +++++++-- nf7.cc | 1 + 9 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 common/context_owner.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index f8babee..472568e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ target_sources(nf7 common/aggregate_promise.hh common/audio_queue.hh common/config.hh + common/context_owner.hh common/dir.hh common/dir_item.hh common/factory.hh diff --git a/common/context_owner.hh b/common/context_owner.hh new file mode 100644 index 0000000..cb8ad1e --- /dev/null +++ b/common/context_owner.hh @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "nf7.hh" + + +namespace nf7 { + +class ContextOwner final { + public: + ContextOwner() = default; + ~ContextOwner() noexcept { + AbortAll(); + } + ContextOwner(const ContextOwner&) = delete; + ContextOwner(ContextOwner&&) = default; + ContextOwner& operator=(const ContextOwner&) = delete; + ContextOwner& operator=(ContextOwner&&) = default; + + template + std::shared_ptr Create(Args&&... args) noexcept { + static_assert(std::is_base_of::value); + + ctx_.erase( + std::remove_if(ctx_.begin(), ctx_.end(), [](auto& x) { return x.expired(); }), + ctx_.end()); + + auto ret = std::make_shared(std::forward(args)...); + ctx_.emplace_back(ret); + return ret; + } + + void AbortAll() noexcept { + for (auto& wctx : ctx_) { + if (auto ctx = wctx.lock()) { + ctx->Abort(); + } + } + } + + private: + std::vector> ctx_; +}; + +} // namespace nf7 diff --git a/common/luajit_nfile_importer.hh b/common/luajit_nfile_importer.hh index a1fced4..081c685 100644 --- a/common/luajit_nfile_importer.hh +++ b/common/luajit_nfile_importer.hh @@ -27,15 +27,14 @@ class NFileImporter : } nf7::Future> Import( - const luajit::Thread& th, std::string_view name) noexcept { + const std::shared_ptr& th, std::string_view name) noexcept { auto self = shared_from_this(); const auto path = base_ / std::string {name}; - auto ljq = th.ljq(); + auto ljq = th->ljq(); auto ctx = std::make_shared< - nf7::GenericContext>(th.env(), th.ctx()->initiator(), - "LuaJIT imported script (nfile)", th.ctx()); + nf7::GenericContext>(th->env(), th->initiator(), "imported LuaJIT script", th); nf7::Future>::Promise pro {ctx}; // create new thread @@ -50,7 +49,7 @@ class NFileImporter : }); auto th_sub = std::make_shared< nf7::luajit::Thread>(ctx, ljq, std::move(handler)); - th_sub->Install(th); + th_sub->Install(*th); // install new importer for sub thread auto dir = path; diff --git a/common/luajit_thread.cc b/common/luajit_thread.cc index c0ee2ce..06b2cfa 100644 --- a/common/luajit_thread.cc +++ b/common/luajit_thread.cc @@ -23,7 +23,7 @@ lua_State* Thread::Init(lua_State* L) noexcept { assert(state_ == kInitial); th_ = lua_newthread(L); - th_ref_.emplace(ctx_, ljq_, L); + th_ref_.emplace(shared_from_this(), ljq_, L); state_ = kPaused; return th_; @@ -136,7 +136,7 @@ static void PushMeta(lua_State* L) noexcept { return luaL_error(L, "import is not available in the current thread"); } if (const auto name = lua_tostring(L, 2)) { - auto fu = im->Import(*th, name); + auto fu = im->Import(th, name); fu.ThenIf([L, th](auto& obj) { th->ExecResume(L, obj); }).template Catch([L, th](auto&) { @@ -155,10 +155,10 @@ static void PushMeta(lua_State* L) noexcept { // nf7:resolve(path) lua_pushcfunction(L, [](auto L) { auto th = Thread::GetPtr(L, 1); - auto base = th->ctx()->initiator(); + auto base = th->initiator(); std::string path = luaL_checkstring(L, 2); - th->env().ExecSub(th->ctx(), [th, L, base, path = std::move(path)]() { + th->env().ExecSub(th, [th, L, base, path = std::move(path)]() { try { th->ExecResume(L, th->env().GetFileOrThrow(base).ResolveOrThrow(path).id()); } catch (nf7::File::NotFoundException&) { @@ -174,7 +174,7 @@ static void PushMeta(lua_State* L) noexcept { auto th = Thread::GetPtr(L, 1); lua_pushvalue(L, 2); - auto ref = std::make_shared(th->ctx(), th->ljq(), L); + auto ref = std::make_shared(th, th->ljq(), L); PushValue(L, nf7::Value {std::move(ref)}); return 1; }); @@ -186,13 +186,13 @@ static void PushMeta(lua_State* L) noexcept { const auto id = luaL_checkinteger(L, 2); std::string iface = luaL_checkstring(L, 3); - th->env().ExecSub(th->ctx(), [th, L, id, iface = std::move(iface)]() { + th->env().ExecSub(th, [th, L, id, iface = std::move(iface)]() { try { auto& f = th->env().GetFileOrThrow(static_cast(id)); if (iface == "node") { th->ExecResume( L, nf7::NodeRootLambda::Create( - th->ctx(), f.template interfaceOrThrow())); + th, f.template interfaceOrThrow())); } else { throw nf7::Exception {"unknown interface: "+iface}; } @@ -211,7 +211,7 @@ static void PushMeta(lua_State* L) noexcept { const auto time = nf7::Env::Clock::now() + std::chrono::milliseconds(static_cast(sec*1000)); - th->ljq()->Push(th->ctx(), [th, L](auto) { th->ExecResume(L); }, time); + th->ljq()->Push(th, [th, L](auto) { th->ExecResume(L); }, time); return th->Yield(L); }); diff --git a/common/luajit_thread.hh b/common/luajit_thread.hh index 972a013..4e403a3 100644 --- a/common/luajit_thread.hh +++ b/common/luajit_thread.hh @@ -14,6 +14,7 @@ #include "nf7.hh" +#include "common/context_owner.hh" #include "common/future.hh" #include "common/logger_ref.hh" #include "common/luajit.hh" @@ -23,7 +24,8 @@ namespace nf7::luajit { -class Thread final : public std::enable_shared_from_this { +class Thread final : public nf7::Context, + public std::enable_shared_from_this { public: static constexpr const char* kTypeName = "nf7::luajit::Thread"; @@ -60,10 +62,12 @@ class Thread final : public std::enable_shared_from_this { } Thread() = delete; - Thread(const std::shared_ptr& ctx, + Thread(const std::shared_ptr& parent, const std::shared_ptr& ljq, Handler&& handler) noexcept : - ctx_(ctx), ljq_(ljq), handler_(std::move(handler)) { + nf7::Context(parent->env(), parent->initiator(), parent), + ljq_(ljq), + handler_(std::move(handler)) { } Thread(const Thread&) = delete; Thread(Thread&&) = delete; @@ -106,20 +110,23 @@ class Thread final : public std::enable_shared_from_this { } // thread-safe - void Abort() noexcept; + void Abort() noexcept override; // queue a task that exec Resume() // thread-safe template void ExecResume(lua_State* L, Args&&... args) noexcept { auto self = shared_from_this(); - ljq_->Push(ctx_, [this, L, self, args...](auto) mutable { + ljq_->Push(self, [this, L, args...](auto) mutable { Resume(L, luajit::PushAll(L, std::forward(args)...)); }); } - nf7::Env& env() const noexcept { return ctx_->env(); } - const std::shared_ptr& ctx() const noexcept { return ctx_; } + + std::string GetDescription() const noexcept override { + return "LuaJIT thread"; + } + const std::shared_ptr& ljq() const noexcept { return ljq_; } const std::shared_ptr& logger() const noexcept { return logger_; } const std::shared_ptr& importer() const noexcept { return importer_; } @@ -129,7 +136,6 @@ class Thread final : public std::enable_shared_from_this { // initialized by constructor std::mutex mtx_; - std::shared_ptr ctx_; std::shared_ptr ljq_; Handler handler_; @@ -163,7 +169,7 @@ class Thread::Importer { // be called on luajit thread virtual nf7::Future> Import( - const luajit::Thread&, std::string_view) noexcept = 0; + const std::shared_ptr&, std::string_view) noexcept = 0; }; diff --git a/common/node_root_lambda.hh b/common/node_root_lambda.hh index 3f00f07..87a9cde 100644 --- a/common/node_root_lambda.hh +++ b/common/node_root_lambda.hh @@ -30,7 +30,11 @@ class NodeRootLambda : public nf7::Node::Lambda, ret->target_ = n.CreateLambda(ret); return ret; } + using nf7::Node::Lambda::Lambda; + ~NodeRootLambda() noexcept { + Abort(); + } void Handle(const nf7::Node::Lambda::Msg& in) noexcept override { std::unique_lock lk {mtx_}; @@ -71,6 +75,10 @@ class NodeRootLambda : public nf7::Node::Lambda, return pro_->future(); } + void Abort() noexcept override { + target_->Abort(); + } + private: std::mutex mtx_; std::shared_ptr target_; diff --git a/file/luajit_node.cc b/file/luajit_node.cc index 7a484da..d722a8a 100644 --- a/file/luajit_node.cc +++ b/file/luajit_node.cc @@ -117,6 +117,8 @@ class Node final : public nf7::FileBase, std::filesystem::file_time_type last_build_ = {}; std::shared_ptr importer_; + + nf7::ContextOwner la_owner_; }; @@ -139,6 +141,10 @@ class Node::Lambda final : public nf7::Node::Lambda, } catch (nf7::ExpiredException&) { } + void Abort() noexcept override { + th_owner_.AbortAll(); + } + private: nf7::Life::Ref f_; @@ -147,6 +153,8 @@ class Node::Lambda final : public nf7::Node::Lambda, std::mutex mtx_; std::optional ctx_; + nf7::ContextOwner th_owner_; + void StartThread(const nf7::Node::Lambda::Msg& in, const std::shared_ptr& func) noexcept { @@ -154,7 +162,7 @@ class Node::Lambda final : public nf7::Node::Lambda, auto self = shared_from_this(); auto hndl = nf7::luajit::Thread::CreateNodeLambdaHandler(in.sender, self); - auto th = std::make_shared(self, ljq, std::move(hndl)); + auto th = th_owner_.Create(self, ljq, std::move(hndl)); th->Install(log_); th->Install(f_->importer_); @@ -176,7 +184,7 @@ class Node::Lambda final : public nf7::Node::Lambda, }; std::shared_ptr Node::CreateLambda( const std::shared_ptr& parent) noexcept { - return std::make_shared(*this, parent); + return la_owner_.Create(*this, parent); } diff --git a/file/node_singleton.cc b/file/node_singleton.cc index fd44e42..fdf6004 100644 --- a/file/node_singleton.cc +++ b/file/node_singleton.cc @@ -120,6 +120,9 @@ class Singleton::SharedLambda final : public nf7::Node::Lambda, public: SharedLambda(Singleton& f) noexcept : nf7::Node::Lambda(f), f_(f.life_) { } + ~SharedLambda() noexcept { + Abort(); + } void SendToTarget(const nf7::Node::Lambda::Msg& in) noexcept try { @@ -135,10 +138,14 @@ class Singleton::SharedLambda final : public nf7::Node::Lambda, } catch (nf7::ExpiredException&) { } catch (nf7::Exception&) { f_->log_.Error("failed to call target"); + Abort(); } - void Drop() noexcept { - target_ = nullptr; + void Abort() noexcept override { + if (target_) { + target_->Abort(); + target_ = nullptr; + } } std::string GetDescription() const noexcept override { @@ -202,7 +209,7 @@ std::shared_ptr Singleton::CreateLambda( void Singleton::UpdateMenu() noexcept { if (ImGui::MenuItem("drop current lambda")) { - shared_la_->Drop(); + shared_la_->Abort(); } } void Singleton::UpdateTooltip() noexcept { diff --git a/nf7.cc b/nf7.cc index de32287..75fc5f8 100644 --- a/nf7.cc +++ b/nf7.cc @@ -1,6 +1,7 @@ #include "nf7.hh" #include +#include #include #include #include