fix an issue that active LuaJIT thread prevents Nf7 from shutting down

This commit is contained in:
falsycat 2022-11-15 15:02:07 +09:00
parent 5c74c5cc40
commit 3c67497229
9 changed files with 107 additions and 27 deletions

View File

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

50
common/context_owner.hh Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#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 <typename T, typename... Args>
std::shared_ptr<T> Create(Args&&... args) noexcept {
static_assert(std::is_base_of<nf7::Context, T>::value);
ctx_.erase(
std::remove_if(ctx_.begin(), ctx_.end(), [](auto& x) { return x.expired(); }),
ctx_.end());
auto ret = std::make_shared<T>(std::forward<Args>(args)...);
ctx_.emplace_back(ret);
return ret;
}
void AbortAll() noexcept {
for (auto& wctx : ctx_) {
if (auto ctx = wctx.lock()) {
ctx->Abort();
}
}
}
private:
std::vector<std::weak_ptr<nf7::Context>> ctx_;
};
} // namespace nf7

View File

@ -27,15 +27,14 @@ class NFileImporter :
}
nf7::Future<std::shared_ptr<luajit::Ref>> Import(
const luajit::Thread& th, std::string_view name) noexcept {
const std::shared_ptr<luajit::Thread>& 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<std::shared_ptr<luajit::Ref>>::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;

View File

@ -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<nf7::Exception>([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<nf7::luajit::Ref>(th->ctx(), th->ljq(), L);
auto ref = std::make_shared<nf7::luajit::Ref>(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<nf7::File::Id>(id));
if (iface == "node") {
th->ExecResume(
L, nf7::NodeRootLambda::Create(
th->ctx(), f.template interfaceOrThrow<nf7::Node>()));
th, f.template interfaceOrThrow<nf7::Node>()));
} 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<uint64_t>(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);
});

View File

@ -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<Thread> {
class Thread final : public nf7::Context,
public std::enable_shared_from_this<Thread> {
public:
static constexpr const char* kTypeName = "nf7::luajit::Thread";
@ -60,10 +62,12 @@ class Thread final : public std::enable_shared_from_this<Thread> {
}
Thread() = delete;
Thread(const std::shared_ptr<nf7::Context>& ctx,
Thread(const std::shared_ptr<nf7::Context>& parent,
const std::shared_ptr<nf7::luajit::Queue>& 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> {
}
// thread-safe
void Abort() noexcept;
void Abort() noexcept override;
// queue a task that exec Resume()
// thread-safe
template <typename... Args>
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>(args)...));
});
}
nf7::Env& env() const noexcept { return ctx_->env(); }
const std::shared_ptr<nf7::Context>& ctx() const noexcept { return ctx_; }
std::string GetDescription() const noexcept override {
return "LuaJIT thread";
}
const std::shared_ptr<nf7::luajit::Queue>& ljq() const noexcept { return ljq_; }
const std::shared_ptr<nf7::LoggerRef>& logger() const noexcept { return logger_; }
const std::shared_ptr<Importer>& importer() const noexcept { return importer_; }
@ -129,7 +136,6 @@ class Thread final : public std::enable_shared_from_this<Thread> {
// initialized by constructor
std::mutex mtx_;
std::shared_ptr<nf7::Context> ctx_;
std::shared_ptr<nf7::luajit::Queue> ljq_;
Handler handler_;
@ -163,7 +169,7 @@ class Thread::Importer {
// be called on luajit thread
virtual nf7::Future<std::shared_ptr<luajit::Ref>> Import(
const luajit::Thread&, std::string_view) noexcept = 0;
const std::shared_ptr<luajit::Thread>&, std::string_view) noexcept = 0;
};

View File

@ -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<std::mutex> 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<nf7::Node::Lambda> target_;

View File

@ -117,6 +117,8 @@ class Node final : public nf7::FileBase,
std::filesystem::file_time_type last_build_ = {};
std::shared_ptr<nf7::luajit::NFileImporter> 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<Node>::Ref f_;
@ -147,6 +153,8 @@ class Node::Lambda final : public nf7::Node::Lambda,
std::mutex mtx_;
std::optional<nf7::luajit::Ref> ctx_;
nf7::ContextOwner th_owner_;
void StartThread(const nf7::Node::Lambda::Msg& in,
const std::shared_ptr<nf7::luajit::Ref>& 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<nf7::luajit::Thread>(self, ljq, std::move(hndl));
auto th = th_owner_.Create<nf7::luajit::Thread>(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<nf7::Node::Lambda> Node::CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
return std::make_shared<Lambda>(*this, parent);
return la_owner_.Create<Lambda>(*this, parent);
}

View File

@ -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<nf7::Node::Lambda> Singleton::CreateLambda(
void Singleton::UpdateMenu() noexcept {
if (ImGui::MenuItem("drop current lambda")) {
shared_la_->Drop();
shared_la_->Abort();
}
}
void Singleton::UpdateTooltip() noexcept {

1
nf7.cc
View File

@ -1,6 +1,7 @@
#include "nf7.hh"
#include <algorithm>
#include <atomic>
#include <cassert>
#include <map>
#include <sstream>