fix an issue that active LuaJIT thread prevents Nf7 from shutting down
This commit is contained in:
parent
5c74c5cc40
commit
3c67497229
@ -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
50
common/context_owner.hh
Normal 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
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user