implement Env

This commit is contained in:
falsycat 2022-05-21 13:18:11 +09:00
parent 8a07d83b0d
commit c486ea78a1
4 changed files with 294 additions and 28 deletions

View File

@ -39,6 +39,8 @@ target_sources(nf7
main.cc
nf7.cc
nf7.hh
common/queue.hh
)
target_link_libraries(nf7
PRIVATE

81
common/queue.hh Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <condition_variable>
#include <deque>
#include <functional>
#include <mutex>
#include <optional>
namespace nf7 {
// thread-safe std::deque wrapper
template <typename T>
class Queue {
public:
Queue() = default;
Queue(const Queue&) = default;
Queue(Queue&&) = default;
Queue& operator=(const Queue&) = default;
Queue& operator=(Queue&&) = default;
void Push(T&& task) noexcept {
std::unique_lock<std::mutex> _(mtx_);
tasks_.push_back(std::move(task));
}
std::optional<T> Pop() noexcept {
std::unique_lock<std::mutex> k(mtx_);
if (tasks_.empty()) return std::nullopt;
auto ret = std::move(tasks_.front());
tasks_.pop_front();
k.unlock();
return ret;
}
protected:
std::mutex mtx_;
private:
std::deque<T> tasks_;
};
// Queue<T> with Wait() method
template <typename T>
class WaitQueue : private Queue<T> {
public:
WaitQueue() = default;
WaitQueue(const WaitQueue&) = default;
WaitQueue(WaitQueue&&) = default;
WaitQueue& operator=(const WaitQueue&) = default;
WaitQueue& operator=(WaitQueue&&) = default;
void Push(T&& task) noexcept {
Queue<T>::Push(std::move(task));
cv_.notify_all();
}
using Queue<T>::Pop;
void Notify() noexcept {
cv_.notify_all();
}
void Wait() noexcept {
std::unique_lock<std::mutex> k(mtx_);
cv_.wait(k);
}
void WaitFor(auto dur) noexcept {
std::unique_lock<std::mutex> k(mtx_);
cv_.wait_for(k, dur);
}
void WaitUntil(auto time) noexcept {
std::unique_lock<std::mutex> k(mtx_);
cv_.wait_until(k, time);
}
private:
using Queue<T>::mtx_;
std::condition_variable cv_;
};
} // namespace nf7

216
main.cc
View File

@ -1,3 +1,7 @@
#include <algorithm>
#include <filesystem>
#include <thread>
#include <GL/glew.h>
#include <imgui.h>
#include <imgui_impl_glfw.h>
@ -6,9 +10,182 @@
#include "nf7.hh"
#include "common/queue.hh"
#include <GLFW/glfw3.h>
using namespace nf7;
class Env final : public nf7::Env {
public:
static constexpr size_t kSubTaskUnit = 64;
Env() noexcept : nf7::Env(std::filesystem::current_path()) {
// start threads
main_thread_ = std::thread([this]() { MainThread(); });
async_threads_.resize(std::max<size_t>(std::thread::hardware_concurrency(), 2));
for (auto& th : async_threads_) {
th = std::thread([this]() { AsyncThread(); });
}
}
~Env() noexcept {
alive_ = false;
cv_.notify_one();
async_.Notify();
main_thread_.join();
for (auto& th : async_threads_) th.join();
}
void ExecMain(Context::Id, Task&& task) noexcept override {
main_.Push(std::move(task));
}
void ExecSub(Context::Id, Task&& task) noexcept override {
sub_.Push(std::move(task));
}
void ExecAsync(Context::Id, Task&& task) noexcept override {
async_.Push(std::move(task));
}
void Update() noexcept {
interrupt_ = true;
std::unique_lock<std::mutex> _(mtx_);
ImGui::PushID(this);
UpdatePanic();
ImGui::PopID();
cv_.notify_one();
}
protected:
File& GetFile(File::Id id) const override {
auto itr = files_.find(id);
if (itr == files_.end()) {
throw ExpiredException("file ("+std::to_string(id)+") is expired");
}
return *itr->second;
}
File::Id AddFile(File& f) noexcept override {
auto [itr, ok] = files_.emplace(file_next_++, &f);
assert(ok);
return itr->first;
}
void RemoveFile(File::Id id) noexcept override {
files_.erase(id);
}
Context& GetContext(Context::Id id) const override {
auto itr = ctxs_.find(id);
if (itr == ctxs_.end()) {
throw ExpiredException("context ("+std::to_string(id)+") is expired");
}
return *itr->second;
}
Context::Id AddContext(Context& ctx) noexcept override {
auto [itr, ok] = ctxs_.emplace(ctx_next_++, &ctx);
assert(ok);
return itr->first;
}
void RemoveContext(Context::Id id) noexcept override {
ctxs_.erase(id);
}
private:
std::atomic<bool> alive_ = true;
std::exception_ptr panic_;
File::Id file_next_ = 1;
std::unordered_map<File::Id, File*> files_;
Context::Id ctx_next_ = 1;
std::unordered_map<Context::Id, Context*> ctxs_;
Queue<Task> main_;
Queue<Task> sub_;
WaitQueue<Task> async_;
std::mutex mtx_;
std::condition_variable cv_;
std::atomic<bool> interrupt_;
std::thread main_thread_;
std::vector<std::thread> async_threads_;
void Panic(std::exception_ptr ptr = std::current_exception()) noexcept {
panic_ = ptr;
}
void UpdatePanic() noexcept {
if (ImGui::BeginPopup("panic")) {
ImGui::TextUnformatted("something went wrong X(");
ImGui::BeginGroup();
{
auto ptr = panic_;
while (ptr)
try {
std::rethrow_exception(ptr);
} catch (Exception& e) {
e.UpdatePanic();
}
}
ImGui::EndGroup();
if (ImGui::Button("abort")) {
std::abort();
}
ImGui::SameLine();
if (ImGui::Button("ignore")) {
panic_ = {};
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
} else {
if (panic_) ImGui::OpenPopup("panic");
}
}
void MainThread() noexcept {
std::unique_lock<std::mutex> k(mtx_);
while (alive_) {
// exec main tasks
while (auto task = main_.Pop())
try {
(*task)();
} catch (Exception&) {
Panic();
}
// exec sub tasks until interrupted
while (!interrupt_) {
for (size_t i = 0; i < kSubTaskUnit; ++i) {
const auto task = sub_.Pop();
if (!task) break;
try {
(*task)();
} catch (Exception&) {
Panic();
}
}
}
cv_.wait(k);
}
}
void AsyncThread() noexcept {
while (alive_) {
while (auto task = async_.Pop())
try {
(*task)();
} catch (Exception&) {
Panic();
}
async_.Wait();
}
}
};
int main(int, char**) {
// init display
glfwSetErrorCallback(
@ -52,27 +229,30 @@ int main(int, char**) {
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
// main loop
while (true) {
// new frame
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// main logic
{
::Env env;
glfwShowWindow(window);
while (!glfwWindowShouldClose(window)) {
// new frame
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// TODO: update
env.Update();
// render windows
ImGui::Render();
int w, h;
glfwGetFramebufferSize(window, &w, &h);
glViewport(0, 0, w, h);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
// TODO: handle queue
// render windows
ImGui::Render();
int w, h;
glfwGetFramebufferSize(window, &w, &h);
glViewport(0, 0, w, h);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
}
}
// teardown ImGUI
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();

23
nf7.hh
View File

@ -182,8 +182,6 @@ class Env {
public:
class Watcher;
using Task = std::function<void()>;
Env() = delete;
Env(const std::filesystem::path& npath) noexcept : npath_(npath) {
}
@ -193,21 +191,26 @@ class Env {
Env& operator=(const Env&) = delete;
Env& operator=(Env&&) = delete;
virtual File::Id AddFile(File&) noexcept = 0;
virtual File& RemoveFile(File::Id) noexcept = 0;
virtual File& GetFile(File::Id) = 0;
virtual File& GetFile(File::Id) const = 0;
virtual Context& GetContext(Context::Id) const = 0;
virtual Context::Id AddContext(Context&) noexcept = 0;
virtual Context& RemoveContext(Context::Id) noexcept = 0;
virtual Context& GetContext(Context::Id) = 0;
// thread-safe
// all Exec*() methods are thread-safe
using Task = std::function<void()>;
virtual void ExecMain(Context::Id, Task&&) noexcept = 0;
virtual void ExecSub(Context::Id, Task&&) noexcept = 0;
virtual void ExecAsync(Context::Id, Task&&) noexcept = 0;
const std::filesystem::path& npath() const noexcept { return npath_; }
protected:
friend class File;
virtual File::Id AddFile(File&) noexcept = 0;
virtual void RemoveFile(File::Id) noexcept = 0;
friend class Context;
virtual Context::Id AddContext(Context&) noexcept = 0;
virtual void RemoveContext(Context::Id) noexcept = 0;
private:
std::filesystem::path npath_;
};