implement Node interface to System/NativeFile

and remove some useless headers
This commit is contained in:
falsycat 2022-08-25 01:45:35 +09:00
parent cec3f79321
commit 1713b11ac8
12 changed files with 253 additions and 777 deletions

View File

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

View File

@ -1,40 +0,0 @@
#pragma once
#include <cstdint>
#include <exception>
#include <memory>
#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<size_t> Read(size_t offset, uint8_t* buf, size_t size) noexcept = 0;
virtual nf7::Future<size_t> Write(size_t offset, const uint8_t* buf, size_t size) noexcept = 0;
virtual nf7::Future<size_t> Truncate(size_t) noexcept = 0;
virtual nf7::Future<size_t> size() const noexcept = 0;
virtual Buffer::Flags flags() const noexcept = 0;
virtual std::shared_ptr<AsyncBuffer> self(AsyncBuffer* = nullptr) noexcept = 0;
protected:
using nf7::Lock::Resource::OnLock;
using nf7::Lock::Resource::OnUnlock;
};
} // namespace nf7

View File

@ -1,105 +0,0 @@
#pragma once
#include <functional>
#include <memory>
#include <mutex>
#include <utility>
#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<AsyncBufferAdaptor> {
public:
AsyncBufferAdaptor(const std::shared_ptr<nf7::Context>& ctx,
const std::shared_ptr<nf7::Buffer>& buf) noexcept :
ctx_(ctx), buf_(buf) {
}
nf7::Future<size_t> Read(size_t offset, uint8_t* ptr, size_t size) noexcept override {
nf7::Future<size_t>::Promise pro(ctx_);
Exec([pro, buf = buf_, offset, ptr, size]() mutable {
pro.Wrap([&]() { return buf->Read(offset, ptr, size); });
});
return pro.future();
}
nf7::Future<size_t> Write(size_t offset, const uint8_t* ptr, size_t size) noexcept override {
nf7::Future<size_t>::Promise pro(ctx_);
Exec([pro, buf = buf_, offset, ptr, size]() mutable {
pro.Wrap([&]() { return buf->Write(offset, ptr, size); });
});
return pro.future();
}
nf7::Future<size_t> Truncate(size_t size) noexcept override {
nf7::Future<size_t>::Promise pro(ctx_);
Exec([pro, buf = buf_, size]() mutable {
pro.Wrap([&]() { return buf->Truncate(size); });
});
return pro.future();
}
nf7::Future<size_t> size() const noexcept override {
nf7::Future<size_t>::Promise pro(ctx_);
const_cast<AsyncBufferAdaptor&>(*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<AsyncBuffer> 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<nf7::Context> ctx_;
std::shared_ptr<nf7::Buffer> buf_;
std::mutex mtx_;
bool working_ = false;
nf7::Queue<std::function<void()>> q_;
void Exec(std::function<void()>&& f) noexcept {
q_.Push(std::move(f));
std::unique_lock<std::mutex> k(mtx_);
if (!std::exchange(working_, true)) {
ctx_->env().ExecAsync(
ctx_, [self = shared_from_this()]() { self->Handle(); });
}
}
void Handle() noexcept {
std::unique_lock<std::mutex> 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

View File

@ -1,40 +0,0 @@
#pragma once
#include <cstdint>
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

View File

@ -1,112 +0,0 @@
#pragma once
#include <cassert>
#include <coroutine>
#include <deque>
#include <exception>
#include <memory>
#include <utility>
#include <vector>
#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::Exception>({"lock cancelled"}));
}
}
Resource(const Resource&) = delete;
Resource(Resource&&) = delete;
Resource& operator=(const Resource&) = delete;
Resource& operator=(Resource&&) = delete;
nf7::Future<std::shared_ptr<Lock>> 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<Lock> TryAcquireLock(bool ex) noexcept {
if (auto k = lock_.lock()) {
return !ex && !k->ex_ && pends_.empty()? k: nullptr;
}
auto k = std::make_shared<Lock>(*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<std::shared_ptr<Lock>>::Promise pro;
};
std::weak_ptr<Lock> lock_;
std::deque<Pending> 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<Lock>(*res_, next.ex);
res_->lock_ = lock;
next.pro.Return(std::move(lock));
}
} // namespace nf7

View File

@ -1,13 +1,10 @@
#include "common/luajit_thread.hh"
#include "common/luajit_thread_lambda.hh"
#include "common/luajit_thread_lock.hh"
#include <chrono>
#include <sstream>
#include <tuple>
#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<nf7::File::Id>(id));
if (iface == "buffer") {
Thread::Lock<nf7::AsyncBuffer>::AcquireAndPush(L, th, f, false);
} else if (iface == "exbuffer") {
Thread::Lock<nf7::AsyncBuffer>::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};

View File

@ -1,190 +0,0 @@
#pragma once
#include "common/luajit_thread.hh"
#include <memory>
#include <utility>
#include "nf7.hh"
#include "common/async_buffer.hh"
#include "common/lock.hh"
#include "common/luajit.hh"
namespace nf7::luajit {
template <typename T>
class Thread::Lock final : public Thread::RegistryItem,
public std::enable_shared_from_this<Thread::Lock<T>> {
public:
using Res = T;
static void AcquireAndPush(
lua_State* L, const std::shared_ptr<Thread>& th, nf7::File& f, bool ex) {
auto res = f.interfaceOrThrow<Res>().self();
res->AcquireLock(ex).Then([L, th, res](auto fu) {
try {
auto k = std::make_shared<Thread::Lock<Res>>(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<Thread>& th,
const std::shared_ptr<Res>& res,
const std::shared_ptr<nf7::Lock>& lock) :
th_(th), res_(res), lock_(lock) {
}
void Push(lua_State* L) noexcept {
luajit::PushWeakPtr<Thread::Lock<Res>>(L, Thread::Lock<T>::shared_from_this());
PushMeta(L);
lua_setmetatable(L, -2);
}
private:
std::weak_ptr<Thread> th_;
std::shared_ptr<Res> res_;
std::shared_ptr<nf7::Lock> 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<nf7::AsyncBuffer>::PushMeta(lua_State* L) noexcept {
constexpr const char* kCustomTypeName =
"nf7::luajit::Thread::Lock<nf7::AsyncBuffer>";
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<Lock>(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_t>(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<std::vector<uint8_t>> vec;
if (auto src = ToMutableVector(L, 4)) {
vec = std::make_shared<std::vector<uint8_t>>(std::move(*src));
vec->resize(static_cast<size_t>(size));
} else {
vec = std::make_shared<std::vector<uint8_t>>(size);
}
buf->Read(static_cast<size_t>(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<uint8_t> {}, 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<Lock>(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<size_t>(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<Lock>(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_t>(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<Thread::Lock<Res>>(L);
lua_setfield(L, -2, "unlock");
lua_setfield(L, -2, "__index");
luajit::PushWeakPtrDeleter<Lock>(L);
lua_setfield(L, -2, "__gc");
}
}
} // namespace nf7::luajit

View File

@ -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<uintptr_t> handle_;
uintptr_t handle_;
void Init();
};
} // namespace nf7

View File

@ -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<uint64_t>(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<int>(*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<int>(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<int>(*handle_);
const auto fd = static_cast<int>(handle_);
const auto off = static_cast<off_t>(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<size_t>(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<int>(*handle_);
const auto fd = static_cast<int>(handle_);
const auto off = static_cast<off_t>(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<size_t>(ret);
}
size_t NativeFile::Truncate(size_t size) {
if (!handle_) {
throw nf7::Buffer::IOException("not locked yet");
}
const auto fd = static_cast<int>(*handle_);
const auto fd = static_cast<int>(handle_);
if (ftruncate(fd, static_cast<off_t>(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<int>(*handle_);
const auto ret = lseek(fd, 0, SEEK_END);
if (ret == -1) {
throw nf7::Buffer::IOException("lseek failure");
}
return static_cast<size_t>(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

View File

@ -1,77 +0,0 @@
#pragma once
#include <memory>
#include <optional>
#include "nf7.hh"
#include "common/future.hh"
namespace nf7 {
template <typename T>
class Task : public nf7::Context,
public std::enable_shared_from_this<Task<T>> {
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<Task<T>>::shared_from_this();
}
auto fu() noexcept { return *fu_; }
protected:
virtual typename nf7::Future<T>::Coro Proc() noexcept = 0;
private:
std::optional<typename nf7::Future<T>::Coro> coro_;
std::optional<nf7::Future<T>> fu_;
};
template <typename T>
class Task<T>::Holder final {
public:
Holder() = default;
Holder(const std::shared_ptr<Task<T>>& 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<Task<T>> lock() const noexcept { return ctx_.lock(); }
private:
std::weak_ptr<Task<T>> ctx_;
};
} // namespace nf7

View File

@ -5,6 +5,7 @@
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
@ -120,6 +121,20 @@ class Value {
const ConstTuple tuple() const { return get<Tuple>(); }
const DataPtr& data() const { return get<DataPtr>(); }
template <typename I>
I integer() const {
const auto ret = integer();
if constexpr (std::is_unsigned<I>::value) {
if (ret < 0) {
throw IncompatibleException("integer underflow");
}
} else {
if (ret != static_cast<Integer>(static_cast<I>(ret))) {
throw IncompatibleException("integer out of range");
}
}
return static_cast<I>(ret);
}
const Value& tuple(size_t idx) const {
auto& tup = *tuple();
return idx < tup.size()? tup[idx].second:

View File

@ -1,6 +1,5 @@
#include <chrono>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <string>
@ -12,161 +11,265 @@
#include <imgui.h>
#include <imgui_stdlib.h>
#include <yas/serialize.hpp>
#include <yas/types/std/chrono.hpp>
#include <yas/types/std/string.hpp>
#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<NativeFile> kType = {
"System/NativeFile", {"nf7::AsyncBuffer", "nf7::DirItem"}};
static inline const nf7::GenericTypeInfo<NativeFile> 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<nf7::NativeFile> nfile;
std::atomic<bool> refresh;
};
struct Runner final {
struct Task {
std::shared_ptr<nf7::Node::Lambda> callee;
std::shared_ptr<nf7::Node::Lambda> caller;
std::function<nf7::Value(const std::shared_ptr<SharedData>&)> func;
std::filesystem::path npath;
nf7::NativeFile::Flags flags;
};
Runner(const std::shared_ptr<SharedData>& shared) noexcept :
shared_(shared) {
}
void operator()(Task&&) noexcept;
private:
std::shared_ptr<SharedData> shared_;
};
using Thread = nf7::Thread<Runner, Runner::Task>;
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<SharedData>()),
th_(std::make_shared<Thread>(*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<File> Clone(Env& env) const noexcept override {
return std::make_unique<NativeFile>(env, npath_, mode_);
std::unique_ptr<nf7::File> Clone(Env& env) const noexcept override {
return std::make_unique<NativeFile>(env, Data {data()});
}
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
std::span<const std::string> GetInputs() const noexcept override {
static const std::vector<std::string> kInputs = {"command"};
return kInputs;
}
std::span<const std::string> GetOutputs() const noexcept override {
static const std::vector<std::string> 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<nf7::AsyncBuffer, nf7::DirItem>(t).Select(this, buf_.get());
return InterfaceSelector<nf7::DirItem, nf7::Node>(t).Select(this);
}
private:
std::shared_ptr<nf7::AsyncBufferAdaptor> buf_;
nf7::Life<NativeFile> life_;
std::shared_ptr<SharedData> shared_;
std::shared_ptr<Thread> th_;
// persistent params
std::filesystem::path npath_;
std::string mode_;
std::filesystem::file_time_type lastmod_;
nf7::GenericMemento<Data> 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<nf7::AsyncBufferAdaptor>(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<NativeFile::Lambda> {
public:
Lambda(NativeFile& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
nf7::Node::Lambda(f, parent), f_(f.life_) {
}
void Handle(std::string_view, const nf7::Value& v,
const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept override
try {
f_.EnforceAlive();
const auto type = v.tuple("type").string();
if (type == "read") {
const auto offset = v.tuple("offset").integer<size_t>();
const auto size = v.tuple("size").integer<size_t>();
Push(caller, [offset, size](auto& shared) {
std::vector<uint8_t> 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<size_t>();
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<nf7::Value::Integer>(ret)};
});
} else if (type == "truncate") {
const auto size = v.tuple("size").integer<size_t>();
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<NativeFile>::Ref f_;
void Push(const std::shared_ptr<nf7::Node::Lambda>& 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<nf7::Node::Lambda> NativeFile::CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
return std::make_shared<NativeFile::Lambda>(*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<nf7::GenericContext>(*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();
}