implement Env
This commit is contained in:
parent
8a07d83b0d
commit
c486ea78a1
@ -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
81
common/queue.hh
Normal 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
216
main.cc
@ -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
23
nf7.hh
@ -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_;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user