From 1713b11ac8e764110f2183bbc637ca0f3774e15b Mon Sep 17 00:00:00 2001 From: falsycat Date: Thu, 25 Aug 2022 01:45:35 +0900 Subject: [PATCH] implement Node interface to System/NativeFile and remove some useless headers --- CMakeLists.txt | 5 - common/async_buffer.hh | 40 ----- common/async_buffer_adaptor.hh | 105 ------------ common/buffer.hh | 40 ----- common/lock.hh | 112 ------------- common/luajit_thread.cc | 11 +- common/luajit_thread_lock.hh | 190 --------------------- common/native_file.hh | 46 +++-- common/native_file_unix.cc | 94 ++--------- common/task.hh | 77 --------- common/value.hh | 15 ++ file/system_native_file.cc | 295 ++++++++++++++++++++++----------- 12 files changed, 253 insertions(+), 777 deletions(-) delete mode 100644 common/async_buffer.hh delete mode 100644 common/async_buffer_adaptor.hh delete mode 100644 common/buffer.hh delete mode 100644 common/lock.hh delete mode 100644 common/luajit_thread_lock.hh delete mode 100644 common/task.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index d8592b3..ff71a88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,7 @@ target_sources(nf7 nf7.hh common/aggregate_command.hh - common/async_buffer.hh - common/async_buffer_adaptor.hh common/audio_queue.hh - common/buffer.hh common/dir.hh common/dir_item.hh common/file_base.hh @@ -74,7 +71,6 @@ target_sources(nf7 common/gui_window.hh common/history.hh common/life.hh - common/lock.hh common/logger.hh common/logger_ref.hh common/luajit.hh @@ -94,7 +90,6 @@ target_sources(nf7 common/queue.hh common/sequencer.hh common/squashed_history.hh - common/task.hh common/thread.hh common/timed_queue.hh common/util_string.hh diff --git a/common/async_buffer.hh b/common/async_buffer.hh deleted file mode 100644 index 975a161..0000000 --- a/common/async_buffer.hh +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "nf7.hh" - -#include "common/buffer.hh" -#include "common/future.hh" -#include "common/lock.hh" - - -namespace nf7 { - -class AsyncBuffer : public nf7::File::Interface, public nf7::Lock::Resource { - public: - using IOException = Buffer::IOException; - - AsyncBuffer() = default; - AsyncBuffer(const AsyncBuffer&) = delete; - AsyncBuffer(AsyncBuffer&&) = delete; - AsyncBuffer& operator=(const AsyncBuffer&) = delete; - AsyncBuffer& operator=(AsyncBuffer&&) = delete; - - virtual nf7::Future Read(size_t offset, uint8_t* buf, size_t size) noexcept = 0; - virtual nf7::Future Write(size_t offset, const uint8_t* buf, size_t size) noexcept = 0; - virtual nf7::Future Truncate(size_t) noexcept = 0; - - virtual nf7::Future size() const noexcept = 0; - virtual Buffer::Flags flags() const noexcept = 0; - - virtual std::shared_ptr self(AsyncBuffer* = nullptr) noexcept = 0; - - protected: - using nf7::Lock::Resource::OnLock; - using nf7::Lock::Resource::OnUnlock; -}; - -} // namespace nf7 diff --git a/common/async_buffer_adaptor.hh b/common/async_buffer_adaptor.hh deleted file mode 100644 index 048ddd3..0000000 --- a/common/async_buffer_adaptor.hh +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "nf7.hh" - -#include "common/async_buffer.hh" -#include "common/buffer.hh" -#include "common/future.hh" -#include "common/queue.hh" - - -namespace nf7 { - -class AsyncBufferAdaptor final : - public nf7::AsyncBuffer, public std::enable_shared_from_this { - public: - AsyncBufferAdaptor(const std::shared_ptr& ctx, - const std::shared_ptr& buf) noexcept : - ctx_(ctx), buf_(buf) { - } - - nf7::Future Read(size_t offset, uint8_t* ptr, size_t size) noexcept override { - nf7::Future::Promise pro(ctx_); - Exec([pro, buf = buf_, offset, ptr, size]() mutable { - pro.Wrap([&]() { return buf->Read(offset, ptr, size); }); - }); - return pro.future(); - } - nf7::Future Write(size_t offset, const uint8_t* ptr, size_t size) noexcept override { - nf7::Future::Promise pro(ctx_); - Exec([pro, buf = buf_, offset, ptr, size]() mutable { - pro.Wrap([&]() { return buf->Write(offset, ptr, size); }); - }); - return pro.future(); - } - nf7::Future Truncate(size_t size) noexcept override { - nf7::Future::Promise pro(ctx_); - Exec([pro, buf = buf_, size]() mutable { - pro.Wrap([&]() { return buf->Truncate(size); }); - }); - return pro.future(); - } - - nf7::Future size() const noexcept override { - nf7::Future::Promise pro(ctx_); - const_cast(*this).Exec([pro, buf = buf_]() mutable { - pro.Wrap([&]() { return buf->size(); }); - }); - return pro.future(); - } - Buffer::Flags flags() const noexcept override { - return buf_->flags(); - } - - std::shared_ptr self(AsyncBuffer*) noexcept override { - return shared_from_this(); - } - - protected: - void OnLock() noexcept override { - Exec([buf = buf_]() { return buf->Lock(); }); - } - void OnUnlock() noexcept override { - Exec([buf = buf_]() { return buf->Unlock(); }); - } - - private: - std::shared_ptr ctx_; - std::shared_ptr buf_; - - std::mutex mtx_; - bool working_ = false; - nf7::Queue> q_; - - void Exec(std::function&& f) noexcept { - q_.Push(std::move(f)); - - std::unique_lock k(mtx_); - if (!std::exchange(working_, true)) { - ctx_->env().ExecAsync( - ctx_, [self = shared_from_this()]() { self->Handle(); }); - } - } - void Handle() noexcept { - std::unique_lock k(mtx_); - if (auto task = q_.Pop()) { - k.unlock(); - try { - (*task)(); - } catch (nf7::Exception&) { - // TODO: unhandled exception :( - } - ctx_->env().ExecAsync( - ctx_, [self = shared_from_this()]() { self->Handle(); }); - } else { - working_ = false; - } - } -}; - -} // namespace nf7 diff --git a/common/buffer.hh b/common/buffer.hh deleted file mode 100644 index c17fb19..0000000 --- a/common/buffer.hh +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include - - -namespace nf7 { - -class Buffer { - public: - enum Flag : uint8_t { - kRead = 1 << 0, - kWrite = 1 << 1, - }; - using Flags = uint8_t; - - class IOException; - - Buffer() = default; - virtual ~Buffer() = default; - Buffer(const Buffer&) = delete; - Buffer(Buffer&&) = delete; - Buffer& operator=(const Buffer&) = delete; - Buffer& operator=(Buffer&&) = delete; - - virtual void Lock() = 0; - virtual void Unlock() = 0; - virtual size_t Read(size_t offset, uint8_t* buf, size_t size) = 0; - virtual size_t Write(size_t offset, const uint8_t* buf, size_t size) = 0; - virtual size_t Truncate(size_t size) = 0; - - virtual size_t size() const = 0; - virtual Flags flags() const noexcept = 0; -}; - -class Buffer::IOException : public nf7::Exception { - public: - using Exception::Exception; -}; - -} // namespace nf7 diff --git a/common/lock.hh b/common/lock.hh deleted file mode 100644 index 9c8b745..0000000 --- a/common/lock.hh +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "nf7.hh" - -#include "common/future.hh" - - -namespace nf7 { - -class Lock final { - public: - class Resource; - class Exception : public nf7::Exception { - public: - using nf7::Exception::Exception; - }; - - Lock() = default; - Lock(Resource& res, bool ex) noexcept : res_(&res), ex_(ex) { - } - inline ~Lock() noexcept; - Lock(const Lock&) = delete; - Lock(Lock&&) = delete; - Lock& operator=(const Lock&) = delete; - Lock& operator=(Lock&&) = delete; - - void Validate() const { - if (!res_) throw Lock::Exception("target expired"); - } - - private: - Resource* res_ = nullptr; - bool ex_ = false; -}; - -class Lock::Resource { - public: - friend Lock; - - Resource() = default; - virtual ~Resource() noexcept { - if (auto lock = lock_.lock()) { - lock->res_ = nullptr; - } - for (auto pend : pends_) { - pend.pro.Throw(std::make_exception_ptr({"lock cancelled"})); - } - } - Resource(const Resource&) = delete; - Resource(Resource&&) = delete; - Resource& operator=(const Resource&) = delete; - Resource& operator=(Resource&&) = delete; - - nf7::Future> AcquireLock(bool ex) noexcept { - if (auto ret = TryAcquireLock(ex)) return ret; - - if (ex || pends_.empty() || pends_.back().ex) { - pends_.push_back(ex); - } - return pends_.back().pro.future(); - } - std::shared_ptr TryAcquireLock(bool ex) noexcept { - if (auto k = lock_.lock()) { - return !ex && !k->ex_ && pends_.empty()? k: nullptr; - } - auto k = std::make_shared(*this, ex); - lock_ = k; - OnLock(); - return k; - } - - protected: - virtual void OnLock() noexcept { } - virtual void OnUnlock() noexcept { } - - private: - struct Pending final { - public: - Pending(bool ex_) noexcept : ex(ex_) { } - - bool ex; - nf7::Future>::Promise pro; - }; - std::weak_ptr lock_; - std::deque pends_; -}; - - -Lock::~Lock() noexcept { - if (!res_) return; - if (res_->pends_.empty()) { - res_->OnUnlock(); - return; - } - - auto next = std::move(res_->pends_.front()); - res_->pends_.pop_front(); - - auto lock = std::make_shared(*res_, next.ex); - res_->lock_ = lock; - next.pro.Return(std::move(lock)); -} - -} // namespace nf7 diff --git a/common/luajit_thread.cc b/common/luajit_thread.cc index 51783dc..c65a2b8 100644 --- a/common/luajit_thread.cc +++ b/common/luajit_thread.cc @@ -1,13 +1,10 @@ #include "common/luajit_thread.hh" #include "common/luajit_thread_lambda.hh" -#include "common/luajit_thread_lock.hh" #include #include #include -#include "common/async_buffer.hh" - namespace nf7::luajit { @@ -121,13 +118,7 @@ static void PushMeta(lua_State* L) noexcept { th->env().ExecSub(th->ctx(), [th, L, id, iface = std::move(iface)]() { try { auto& f = th->env().GetFileOrThrow(static_cast(id)); - if (iface == "buffer") { - Thread::Lock::AcquireAndPush(L, th, f, false); - } else if (iface == "exbuffer") { - Thread::Lock::AcquireAndPush(L, th, f, true); - } else if (iface == "lua") { - // WIP GetLuaObjAndPush(L, th, f); - } else if (iface == "node") { + if (iface == "node") { Thread::Lambda::CreateAndPush(L, th, f); } else { throw nf7::Exception {"unknown interface: "+iface}; diff --git a/common/luajit_thread_lock.hh b/common/luajit_thread_lock.hh deleted file mode 100644 index 704df02..0000000 --- a/common/luajit_thread_lock.hh +++ /dev/null @@ -1,190 +0,0 @@ -#pragma once - -#include "common/luajit_thread.hh" - -#include -#include - -#include "nf7.hh" - -#include "common/async_buffer.hh" -#include "common/lock.hh" -#include "common/luajit.hh" - - -namespace nf7::luajit { - -template -class Thread::Lock final : public Thread::RegistryItem, - public std::enable_shared_from_this> { - public: - using Res = T; - - static void AcquireAndPush( - lua_State* L, const std::shared_ptr& th, nf7::File& f, bool ex) { - auto res = f.interfaceOrThrow().self(); - res->AcquireLock(ex).Then([L, th, res](auto fu) { - try { - auto k = std::make_shared>(th, res, fu.value()); - th->ljq()->Push(th->ctx(), [L, th, k](auto) { - th->Register(L, k); - k->Push(L); - th->Resume(L, 1); - }); - } catch (nf7::Exception& e) { - th->ExecResume(L, nullptr, e.msg()); - } - }); - } - - Lock(const std::shared_ptr& th, - const std::shared_ptr& res, - const std::shared_ptr& lock) : - th_(th), res_(res), lock_(lock) { - } - - void Push(lua_State* L) noexcept { - luajit::PushWeakPtr>(L, Thread::Lock::shared_from_this()); - PushMeta(L); - lua_setmetatable(L, -2); - } - - private: - std::weak_ptr th_; - - std::shared_ptr res_; - std::shared_ptr lock_; - - - auto Validate(lua_State* L) { - auto t = th_.lock(); - if (!t) { - luaL_error(L, "thread expired"); - } - t->EnsureActive(L); - try { - lock_->Validate(); - } catch (nf7::Exception& e) { - luaL_error(L, "%s", e.msg().c_str()); - } - return std::make_tuple(t, res_, lock_); - } - - static void PushMeta(lua_State* L) noexcept; -}; - - -template <> -void Thread::Lock::PushMeta(lua_State* L) noexcept { - constexpr const char* kCustomTypeName = - "nf7::luajit::Thread::Lock"; - - constexpr size_t kBufferSizeMax = 1024 * 1024 * 64; - - if (luaL_newmetatable(L, kCustomTypeName)) { - lua_createtable(L, 0, 0); - - // lock:read(offset, bytes [, mutable vector]) -> MutableVector - lua_pushcfunction(L, ([](auto L) { - auto [th, buf, lock] = CheckWeakPtr(L, 1, kCustomTypeName)->Validate(L); - - auto off = luaL_checkinteger(L, 2); - auto size = luaL_checkinteger(L, 3); - - if (off < 0) { - return luaL_error(L, "negative offset"); - } - if (size < 0) { - return luaL_error(L, "negative size"); - } - - const size_t usize = static_cast(size); - if (usize > kBufferSizeMax) { - return luaL_error(L, "too large size is requested"); - } - - // allocates new vector to store result or reuses the passed vector - std::shared_ptr> vec; - if (auto src = ToMutableVector(L, 4)) { - vec = std::make_shared>(std::move(*src)); - vec->resize(static_cast(size)); - } else { - vec = std::make_shared>(size); - } - - buf->Read(static_cast(off), vec->data(), usize). - Then([th, L, vec](auto fu) { - try { - vec->resize(fu.value()); - th->ExecResume(L, std::move(*vec)); - } catch (nf7::Exception& e) { - th->ExecResume(L, std::vector {}, e.msg()); - } - }); - th->ExpectYield(L); - return lua_yield(L, 0); - })); - lua_setfield(L, -2, "read"); - - // lock:write(offset, vector) -> size - lua_pushcfunction(L, ([](auto L) { - auto [th, buf, lock] = CheckWeakPtr(L, 1, kCustomTypeName)->Validate(L); - - auto off = luaL_checkinteger(L, 2); - auto optvec = luajit::ToVector(L, 3); - - if (off < 0) { - return luaL_error(L, "negative offset"); - } - if (!optvec) { - return luaL_error(L, "vector is expected for the third argument"); - } - auto& vec = *optvec; - - buf->Write(static_cast(off), vec->data(), vec->size()). - Then([th, L, vec](auto fu) { - try { - th->ExecResume(L, fu.value()); - } catch (nf7::Exception& e) { - th->ExecResume(L, 0, e.msg()); - } - }); - th->ExpectYield(L); - return lua_yield(L, 0); - })); - lua_setfield(L, -2, "write"); - - // lock:truncate(size) -> size - lua_pushcfunction(L, ([](auto L) { - auto [th, buf, lock] = CheckWeakPtr(L, 1, kCustomTypeName)->Validate(L); - - auto size = luaL_checkinteger(L, 2); - if (size < 0) { - return luaL_error(L, "negative size"); - } - - buf->Truncate(static_cast(size)). - Then([th, L](auto fu) { - try { - th->ExecResume(L, fu.value()); - } catch (nf7::Exception& e) { - th->ExecResume(L, nullptr, e.msg()); - } - }); - th->ExpectYield(L); - return lua_yield(L, 0); - })); - lua_setfield(L, -2, "truncate"); - - // lock:unlock() - luajit::PushWeakPtrDeleter>(L); - lua_setfield(L, -2, "unlock"); - - lua_setfield(L, -2, "__index"); - - luajit::PushWeakPtrDeleter(L); - lua_setfield(L, -2, "__gc"); - } -} - -} // namespace nf7::luajit diff --git a/common/native_file.hh b/common/native_file.hh index 7602631..a91d39e 100644 --- a/common/native_file.hh +++ b/common/native_file.hh @@ -8,54 +8,48 @@ #include "nf7.hh" -#include "common/buffer.hh" - namespace nf7 { -class NativeFile final : public nf7::Buffer, public nf7::Context { +class NativeFile final : public nf7::Context { public: + class Exception final : public nf7::Exception { + using nf7::Exception::Exception; + }; + enum Flag : uint8_t { - kCreateIf = 1 << 0, - kExclusive = 1 << 1, + kRead = 1 << 0, + kWrite = 1 << 1, }; using Flags = uint8_t; NativeFile() = delete; - NativeFile(nf7::File& f, - const std::filesystem::path& path, - Buffer::Flags flags, - Flags nflags) noexcept : - Context(f.env(), f.id()), path_(path), flags_(flags), nflags_(nflags) { + NativeFile(nf7::Env& env, nf7::File::Id id, + const std::filesystem::path& path, Flags flags) : + Context(env, id), path_(path), flags_(flags) { + Init(); } + ~NativeFile() noexcept; NativeFile(const NativeFile&) = delete; NativeFile(NativeFile&&) = delete; NativeFile& operator=(const NativeFile&) = delete; NativeFile& operator=(NativeFile&&) = delete; - void Lock() override; - void Unlock() override; - size_t Read(size_t offset, uint8_t* buf, size_t size) override; - size_t Write(size_t offset, const uint8_t* buf, size_t size) override; - size_t Truncate(size_t size) override; + size_t Read(size_t offset, uint8_t* buf, size_t size); + size_t Write(size_t offset, const uint8_t* buf, size_t size); + size_t Truncate(size_t size); - size_t size() const override; - Buffer::Flags flags() const noexcept override { + Flags flags() const noexcept { return flags_; } - void CleanUp() noexcept override; - void Abort() noexcept override; - - size_t GetMemoryUsage() const noexcept override; - std::string GetDescription() const noexcept override; - private: const std::filesystem::path path_; - const Buffer::Flags flags_; - const NativeFile::Flags nflags_; + const Flags flags_; - std::optional handle_; + uintptr_t handle_; + + void Init(); }; } // namespace nf7 diff --git a/common/native_file_unix.cc b/common/native_file_unix.cc index 3d84b00..d630307 100644 --- a/common/native_file_unix.cc +++ b/common/native_file_unix.cc @@ -11,117 +11,59 @@ extern "C" { namespace nf7 { -void NativeFile::Lock() { - if (handle_) { - throw nf7::Buffer::IOException("already locked"); - } - +void NativeFile::Init() { int flags = 0; - if ((flags_ & nf7::Buffer::kRead) && (flags_ & nf7::Buffer::kWrite)) { - flags |= O_RDWR; - } else if (flags_ & nf7::Buffer::kRead) { + if ((flags_ & kRead) && (flags_ & kWrite)) { + flags |= O_RDWR | O_CREAT; + } else if (flags_ & kRead) { flags |= O_RDONLY; - } else if (flags_ & nf7::Buffer::kWrite) { - flags |= O_WRONLY; + } else if (flags_ & kWrite) { + flags |= O_WRONLY | O_CREAT; } - if (nflags_ & kCreateIf) flags |= O_CREAT; int fd = open(path_.string().c_str(), flags, 0600); if (fd < 0) { - throw nf7::Buffer::IOException("open failure"); + throw NativeFile::Exception {"open failure"}; } handle_ = static_cast(fd); - - if (nflags_ & kExclusive) { - if (flock(fd, LOCK_EX) != 0) { - close(fd); - throw nf7::Buffer::IOException("flock failure"); - } - } } -void NativeFile::Unlock() { - if (!handle_) { - throw nf7::Buffer::IOException("not locked yet"); - } - const auto fd = static_cast(*handle_); - if (nflags_ & kExclusive) { - if (flock(fd, LOCK_UN) != 0) { - close(fd); - throw nf7::Buffer::IOException("flock failure"); - } - } +NativeFile::~NativeFile() noexcept { + const auto fd = static_cast(handle_); if (close(fd) == -1) { - throw nf7::Buffer::IOException("close failure"); + // ;( } - handle_ = std::nullopt; } size_t NativeFile::Read(size_t offset, uint8_t* buf, size_t size) { - if (!handle_) { - throw nf7::Buffer::IOException("not locked yet"); - } - const auto fd = static_cast(*handle_); + const auto fd = static_cast(handle_); const auto off = static_cast(offset); if (lseek(fd, off, SEEK_SET) == off-1) { - throw nf7::Buffer::IOException("lseek failure"); + throw NativeFile::Exception {"lseek failure"}; } const auto ret = read(fd, buf, size); if (ret == -1) { - throw nf7::Buffer::IOException("read failure"); + throw NativeFile::Exception {"read failure"}; } return static_cast(ret); } size_t NativeFile::Write(size_t offset, const uint8_t* buf, size_t size) { - if (!handle_) { - throw nf7::Buffer::IOException("not locked yet"); - } - const auto fd = static_cast(*handle_); + const auto fd = static_cast(handle_); const auto off = static_cast(offset); if (lseek(fd, off, SEEK_SET) == off-1) { - throw nf7::Buffer::IOException("lseek failure"); + throw nf7::NativeFile::Exception {"lseek failure"}; } const auto ret = write(fd, buf, size); if (ret == -1) { - throw nf7::Buffer::IOException("write failure"); + throw nf7::NativeFile::Exception {"write failure"}; } return static_cast(ret); } size_t NativeFile::Truncate(size_t size) { - if (!handle_) { - throw nf7::Buffer::IOException("not locked yet"); - } - const auto fd = static_cast(*handle_); + const auto fd = static_cast(handle_); if (ftruncate(fd, static_cast(size)) == 0) { - throw nf7::Buffer::IOException("ftruncate failure"); + throw nf7::NativeFile::Exception {"ftruncate failure"}; } return size; } -size_t NativeFile::size() const { - if (!handle_) { - throw nf7::Buffer::IOException("not locked yet"); - } - const auto fd = static_cast(*handle_); - const auto ret = lseek(fd, 0, SEEK_END); - if (ret == -1) { - throw nf7::Buffer::IOException("lseek failure"); - } - return static_cast(ret); -} - -void NativeFile::CleanUp() noexcept { -} -void NativeFile::Abort() noexcept { -} -size_t NativeFile::GetMemoryUsage() const noexcept { - return 0; -} -std::string NativeFile::GetDescription() const noexcept { - if (!handle_) { - return "unix file descriptor: "+path_.string(); - } else { - return "unix file descriptor (active): "+path_.string(); - } -} - } // namespace nf7 diff --git a/common/task.hh b/common/task.hh deleted file mode 100644 index 90ead20..0000000 --- a/common/task.hh +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include - -#include "nf7.hh" - -#include "common/future.hh" - - -namespace nf7 { - -template -class Task : public nf7::Context, - public std::enable_shared_from_this> { - public: - class Holder; - - using nf7::Context::Context; - Task(const Task&) = delete; - Task(Task&&) = delete; - Task& operator=(const Task&) = delete; - Task& operator=(Task&&) = delete; - - void Start() noexcept { - coro_ = Proc(); - fu_ = coro_->Start(self()); - } - void Abort() noexcept { - coro_->Abort(); - } - - auto self() noexcept { - return std::enable_shared_from_this>::shared_from_this(); - } - auto fu() noexcept { return *fu_; } - - protected: - virtual typename nf7::Future::Coro Proc() noexcept = 0; - - private: - std::optional::Coro> coro_; - std::optional> fu_; -}; - -template -class Task::Holder final { - public: - Holder() = default; - Holder(const std::shared_ptr>& ctx) noexcept : ctx_(ctx) { - } - ~Holder() noexcept { - Abort(); - } - Holder(const Holder&) = delete; - Holder(Holder&& src) noexcept = default; - Holder& operator=(const Holder&) = delete; - Holder& operator=(Holder&& src) noexcept { - if (this != &src) { - Abort(); - ctx_ = std::move(src.ctx_); - } - return *this; - } - - void Abort() noexcept { - if (auto ctx = ctx_.lock()) ctx->Abort(); - ctx_ = {}; - } - - std::shared_ptr> lock() const noexcept { return ctx_.lock(); } - - private: - std::weak_ptr> ctx_; -}; - -} // namespace nf7 diff --git a/common/value.hh b/common/value.hh index 600eacd..fc5f151 100644 --- a/common/value.hh +++ b/common/value.hh @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,20 @@ class Value { const ConstTuple tuple() const { return get(); } const DataPtr& data() const { return get(); } + template + I integer() const { + const auto ret = integer(); + if constexpr (std::is_unsigned::value) { + if (ret < 0) { + throw IncompatibleException("integer underflow"); + } + } else { + if (ret != static_cast(static_cast(ret))) { + throw IncompatibleException("integer out of range"); + } + } + return static_cast(ret); + } const Value& tuple(size_t idx) const { auto& tup = *tuple(); return idx < tup.size()? tup[idx].second: diff --git a/file/system_native_file.cc b/file/system_native_file.cc index 01d6dc8..1088d3e 100644 --- a/file/system_native_file.cc +++ b/file/system_native_file.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -12,161 +11,265 @@ #include #include #include -#include -#include #include "nf7.hh" -#include "common/async_buffer.hh" -#include "common/async_buffer_adaptor.hh" #include "common/dir_item.hh" #include "common/generic_context.hh" +#include "common/generic_memento.hh" #include "common/generic_type_info.hh" +#include "common/gui_popup.hh" #include "common/gui_window.hh" +#include "common/life.hh" #include "common/native_file.hh" +#include "common/node.hh" #include "common/ptr_selector.hh" -#include "common/queue.hh" +#include "common/thread.hh" #include "common/yas_std_filesystem.hh" namespace nf7 { namespace { -class NativeFile final : public File, - public nf7::DirItem { +class NativeFile final : public nf7::File, + public nf7::DirItem, public nf7::Node { public: - static inline const GenericTypeInfo kType = { - "System/NativeFile", {"nf7::AsyncBuffer", "nf7::DirItem"}}; + static inline const nf7::GenericTypeInfo kType = { + "System/NativeFile", {"nf7::DirItem", "nf7::Node"}}; static void UpdateTypeTooltip() noexcept { - ImGui::TextUnformatted("Reads/Writes a file placed on native filesystem."); - ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::AsyncBuffer"); - ImGui::Bullet(); ImGui::TextUnformatted("basepath is environment native path"); + ImGui::TextUnformatted("Read/Write a file placed on native filesystem."); + ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::Node"); } - NativeFile(Env& env, const std::filesystem::path& path = "", std::string_view mode = "") noexcept : + class Lambda; + + struct SharedData final { + std::optional nfile; + std::atomic refresh; + }; + struct Runner final { + struct Task { + std::shared_ptr callee; + std::shared_ptr caller; + std::function&)> func; + + std::filesystem::path npath; + nf7::NativeFile::Flags flags; + }; + + Runner(const std::shared_ptr& shared) noexcept : + shared_(shared) { + } + void operator()(Task&&) noexcept; + + private: + std::shared_ptr shared_; + }; + using Thread = nf7::Thread; + + struct Data final { + std::filesystem::path npath; + std::string mode; + }; + + NativeFile(nf7::Env& env, Data&& data = {}) noexcept : nf7::File(kType, env), - nf7::DirItem( - nf7::DirItem::kTooltip | - nf7::DirItem::kWidget), - npath_(path), mode_(mode) { + nf7::DirItem(nf7::DirItem::kWidget), + life_(*this), + shared_(std::make_shared()), + th_(std::make_shared(*this, Runner {shared_})), + mem_(std::move(data)), + config_popup_(*this) { + mem_.onRestore = [this]() { Touch(); }; + mem_.onCommit = [this]() { Touch(); }; } - NativeFile(Env& env, Deserializer& ar) : NativeFile(env) { - ar(npath_, mode_, lastmod_); + NativeFile(nf7::Env& env, nf7::Deserializer&) : NativeFile(env) { + // TODO } - void Serialize(Serializer& ar) const noexcept override { - ar(npath_, mode_, lastmod_); + void Serialize(nf7::Serializer&) const noexcept override { } - std::unique_ptr Clone(Env& env) const noexcept override { - return std::make_unique(env, npath_, mode_); + std::unique_ptr Clone(Env& env) const noexcept override { + return std::make_unique(env, Data {data()}); + } + + std::shared_ptr CreateLambda( + const std::shared_ptr&) noexcept override; + + std::span GetInputs() const noexcept override { + static const std::vector kInputs = {"command"}; + return kInputs; + } + std::span GetOutputs() const noexcept override { + static const std::vector kOutputs = {"result"}; + return kOutputs; } void Update() noexcept override; - void UpdateTooltip() noexcept override; void UpdateWidget() noexcept override; - void Handle(const Event& ev) noexcept override { - switch (ev.type) { - case Event::kAdd: - Reset(); - return; - case Event::kRemove: - buf_ = nullptr; - return; - default: - return; - } - } - File::Interface* interface(const std::type_info& t) noexcept override { - return InterfaceSelector(t).Select(this, buf_.get()); + return InterfaceSelector(t).Select(this); } private: - std::shared_ptr buf_; + nf7::Life life_; + + std::shared_ptr shared_; + std::shared_ptr th_; - // persistent params - std::filesystem::path npath_; - std::string mode_; std::filesystem::file_time_type lastmod_; + nf7::GenericMemento mem_; - void Reset() noexcept { - bool exlock = false; - nf7::Buffer::Flags flags = 0; - for (auto c : mode_) { - if (c == 'x') exlock = true; - flags |= - c == 'r'? nf7::Buffer::kRead: - c == 'w'? nf7::Buffer::kWrite: 0; + const Data& data() const noexcept { return mem_.data(); } + Data& data() noexcept { return mem_.data(); } + + + // GUI popup + struct ConfigPopup final : private nf7::gui::Popup { + public: + ConfigPopup(NativeFile& f) noexcept : + nf7::gui::Popup("ConfigPopup"), f_(&f) { } - auto buf = std::make_shared< - nf7::NativeFile>(*this, env().npath()/npath_, flags, exlock); - buf_ = std::make_shared(buf, buf); + + void Open() noexcept { + npath_ = f_->data().npath.generic_string(); + nf7::gui::Popup::Open(); + } + void Update() noexcept; + + private: + NativeFile* const f_; + + std::string npath_; + bool read_, write_; + } config_popup_; +}; + +class NativeFile::Lambda final : public nf7::Node::Lambda, + public std::enable_shared_from_this { + public: + Lambda(NativeFile& f, const std::shared_ptr& parent) noexcept : + nf7::Node::Lambda(f, parent), f_(f.life_) { + } + + void Handle(std::string_view, const nf7::Value& v, + const std::shared_ptr& caller) noexcept override + try { + f_.EnforceAlive(); + + const auto type = v.tuple("type").string(); + if (type == "read") { + const auto offset = v.tuple("offset").integer(); + const auto size = v.tuple("size").integer(); + Push(caller, [offset, size](auto& shared) { + std::vector buf; + buf.resize(size); + const auto actual = shared->nfile->Read(offset, buf.data(), size); + buf.resize(actual); + return nf7::Value {std::move(buf)}; + }); + } else if (type == "write") { + const auto offset = v.tuple("offset").integer(); + const auto buf = v.tuple("buf").vector(); + Push(caller, [offset, buf](auto& shared) { + const auto ret = shared->nfile->Write(offset, buf->data(), buf->size()); + return nf7::Value {static_cast(ret)}; + }); + } else if (type == "truncate") { + const auto size = v.tuple("size").integer(); + Push(caller, [size](auto& shared) { + shared->nfile->Truncate(size); + return nf7::Value::Pulse {}; + }); + } else { + throw nf7::Exception {"unknown command type: "+type}; + } + } catch (nf7::Exception&) { + // TODO log + } + + private: + nf7::Life::Ref f_; + + void Push(const std::shared_ptr& caller, auto&& f) noexcept { + const auto& mode = f_->data().mode; + nf7::NativeFile::Flags flags = 0; + if (std::string::npos != mode.find('r')) flags |= nf7::NativeFile::kRead; + if (std::string::npos != mode.find('w')) flags |= nf7::NativeFile::kWrite; + + auto self = shared_from_this(); + f_->th_->Push(self, NativeFile::Runner::Task { + .callee = self, + .caller = caller, + .func = std::move(f), + .npath = f_->data().npath, + .flags = flags, + }); } }; +std::shared_ptr NativeFile::CreateLambda( + const std::shared_ptr& parent) noexcept { + return std::make_shared(*this, parent); +} +void NativeFile::Runner::operator()(Task&& t) noexcept +try { + auto callee = t.callee; + auto caller = t.caller; + + if (shared_->refresh.exchange(false) || !shared_->nfile) { + shared_->nfile.emplace(callee->env(), callee->initiator(), t.npath, t.flags); + } + auto ret = t.func(shared_); + callee->env().ExecSub(callee, [callee, caller, ret = std::move(ret)]() { + caller->Handle("result", ret, callee); + }); +} catch (nf7::Exception&) { + // TODO log +} + void NativeFile::Update() noexcept { // file update check try { - const auto lastmod = std::filesystem::last_write_time(env().npath()/npath_); + const auto npath = env().npath() / data().npath; + const auto lastmod = std::filesystem::last_write_time(npath); if (std::exchange(lastmod_, lastmod) < lastmod) { Touch(); } } catch (std::filesystem::filesystem_error&) { } } -void NativeFile::UpdateTooltip() noexcept { - ImGui::Text("basepath: %s", env().npath().generic_string().c_str()); - ImGui::Text("path : %s", npath_.generic_string().c_str()); - ImGui::Text("mode : %s", mode_.c_str()); -} void NativeFile::UpdateWidget() noexcept { ImGui::TextUnformatted("System/NativeFile"); - if (ImGui::Button("change referencee")) { - ImGui::OpenPopup("ReplaceReferenceePopup"); + if (ImGui::Button("config")) { + config_popup_.Open(); } - - if (ImGui::BeginPopup("ReplaceReferenceePopup")) { - static std::string path; - static bool flag_exlock; - static bool flag_readable; - static bool flag_writeable; - - ImGui::TextUnformatted("System/NativeFile: config"); - if (ImGui::IsWindowAppearing()) { - path = npath_.generic_string(); - flag_exlock = mode_.find('x') != std::string::npos; - flag_readable = mode_.find('r') != std::string::npos; - flag_writeable = mode_.find('w') != std::string::npos; - } - - ImGui::InputText("path", &path); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "path to the native file system (base: '%s')", - env().npath().generic_string().c_str()); - } - ImGui::Checkbox("exclusive lock", &flag_exlock); - ImGui::Checkbox("readable", &flag_readable); - ImGui::Checkbox("writeable", &flag_writeable); + config_popup_.Update(); +} +void NativeFile::ConfigPopup::Update() noexcept { + if (nf7::gui::Popup::Begin()) { + ImGui::InputText("path", &npath_); + ImGui::Checkbox("read", &read_); + ImGui::Checkbox("write", &write_); if (ImGui::Button("ok")) { ImGui::CloseCurrentPopup(); - npath_ = path; - mode_ = ""; - if (flag_exlock) mode_ += 'x'; - if (flag_readable) mode_ += 'r'; - if (flag_writeable) mode_ += 'w'; + auto& d = f_->data(); + d.npath = npath_; - auto ctx = std::make_shared(*this, "resetting native file handle"); - env().ExecMain(ctx, [this]() { Reset(); Touch(); }); + d.mode = ""; + if (read_) d.mode += "r"; + if (write_) d.mode += "w"; + + f_->mem_.Commit(); } - - if (!std::filesystem::exists(env().npath()/path)) { - ImGui::Bullet(); ImGui::TextUnformatted("target file seems to be missing..."); + if (!std::filesystem::exists(f_->env().npath()/npath_)) { + ImGui::Bullet(); + ImGui::TextUnformatted("file not found"); } ImGui::EndPopup(); }