From 0302bfdc2207f9b5482fb07aa6052e7f6cb237ca Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sun, 27 Apr 2025 00:03:32 +0100 Subject: async: add Promise and use it for AsyncDialogBox --- src/helpers/AsyncDialogBox.cpp | 17 +++--- src/helpers/AsyncDialogBox.hpp | 34 ++++++------ src/helpers/defer/Promise.hpp | 119 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 src/helpers/defer/Promise.hpp (limited to 'src/helpers') diff --git a/src/helpers/AsyncDialogBox.cpp b/src/helpers/AsyncDialogBox.cpp index 73bae8f6..86eb9e1f 100644 --- a/src/helpers/AsyncDialogBox.cpp +++ b/src/helpers/AsyncDialogBox.cpp @@ -59,8 +59,7 @@ void CAsyncDialogBox::onWrite(int fd, uint32_t mask) { if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { Debug::log(LOG, "CAsyncDialogBox: dialog {:x} hung up, closed."); - if (m_onResolution) - m_onResolution(m_stdout); + m_promiseResolver->resolve(m_stdout); wl_event_source_remove(m_readEventSource); m_selfReference.reset(); @@ -68,9 +67,7 @@ void CAsyncDialogBox::onWrite(int fd, uint32_t mask) { } } -void CAsyncDialogBox::open(std::function onResolution) { - m_onResolution = onResolution; - +SP> CAsyncDialogBox::open() { std::string buttonsString = ""; for (auto& b : m_buttons) { buttonsString += b + ";"; @@ -83,7 +80,7 @@ void CAsyncDialogBox::open(std::function onResolution) { int outPipe[2]; if (pipe(outPipe)) { Debug::log(ERR, "CAsyncDialogBox::open: failed to pipe()"); - return; + return nullptr; } m_pipeReadFd = CFileDescriptor(outPipe[0]); @@ -94,7 +91,7 @@ void CAsyncDialogBox::open(std::function onResolution) { if (!m_readEventSource) { Debug::log(ERR, "CAsyncDialogBox::open: failed to add read fd to loop"); - return; + return nullptr; } m_selfReference = m_selfWeakReference.lock(); @@ -102,13 +99,17 @@ void CAsyncDialogBox::open(std::function onResolution) { if (!proc.runAsync()) { Debug::log(ERR, "CAsyncDialogBox::open: failed to run async"); wl_event_source_remove(m_readEventSource); - return; + return nullptr; } m_dialogPid = proc.pid(); // close the write fd, only the dialog owns it now close(outPipe[1]); + + auto promise = CPromise::make([this](SP> r) { m_promiseResolver = r; }); + + return promise; } void CAsyncDialogBox::kill() { diff --git a/src/helpers/AsyncDialogBox.hpp b/src/helpers/AsyncDialogBox.hpp index d3cc242c..0cb95846 100644 --- a/src/helpers/AsyncDialogBox.hpp +++ b/src/helpers/AsyncDialogBox.hpp @@ -2,6 +2,7 @@ #include "../macros.hpp" #include "./memory/Memory.hpp" +#include "./defer/Promise.hpp" #include #include @@ -15,29 +16,30 @@ class CAsyncDialogBox { public: static SP create(const std::string& title, const std::string& description, std::vector buttons); - CAsyncDialogBox(const CAsyncDialogBox&) = delete; - CAsyncDialogBox(CAsyncDialogBox&&) = delete; - CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete; - CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete; + CAsyncDialogBox(const CAsyncDialogBox&) = delete; + CAsyncDialogBox(CAsyncDialogBox&&) = delete; + CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete; + CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete; - void open(std::function onResolution); - void kill(); - bool isRunning() const; + SP> open(); + void kill(); + bool isRunning() const; - void onWrite(int fd, uint32_t mask); + void onWrite(int fd, uint32_t mask); private: CAsyncDialogBox(const std::string& title, const std::string& description, std::vector buttons); - pid_t m_dialogPid = 0; - wl_event_source* m_readEventSource = nullptr; - std::function m_onResolution; - Hyprutils::OS::CFileDescriptor m_pipeReadFd; - std::string m_stdout = ""; + pid_t m_dialogPid = 0; + wl_event_source* m_readEventSource = nullptr; + Hyprutils::OS::CFileDescriptor m_pipeReadFd; + std::string m_stdout = ""; - const std::string m_title; - const std::string m_description; - const std::vector m_buttons; + const std::string m_title; + const std::string m_description; + const std::vector m_buttons; + + SP> m_promiseResolver; // WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers SP m_selfReference; diff --git a/src/helpers/defer/Promise.hpp b/src/helpers/defer/Promise.hpp new file mode 100644 index 00000000..53399bc2 --- /dev/null +++ b/src/helpers/defer/Promise.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include "../memory/Memory.hpp" + +// TODO: move into hyprutils + +template +class CPromise; +template +class CPromiseResult; + +template +class CPromiseResolver { + public: + CPromiseResolver(const CPromiseResolver&) = delete; + CPromiseResolver(CPromiseResolver&&) = delete; + CPromiseResolver& operator=(const CPromiseResolver&) = delete; + CPromiseResolver& operator=(CPromiseResolver&&) = delete; + + void resolve(T value) { + if (m_promise->m_result) + return; + + m_promise->m_result = CPromiseResult::result(value); + + if (!m_promise->m_then) + return; + + m_promise->m_then(m_promise->m_result); + } + + void reject(const std::string& reason) { + if (m_promise->m_result) + return; + + m_promise->m_result = CPromiseResult::err(reason); + + if (!m_promise->m_then) + return; + + m_promise->m_then(m_promise->m_result); + } + + private: + CPromiseResolver(SP> promise) : m_promise(promise) {} + + SP> m_promise; + + friend class CPromise; +}; + +template +class CPromiseResult { + public: + bool hasError() { + return m_hasError; + } + + T result() { + return m_result; + } + + std::string error() { + return m_error; + } + + private: + static SP> result(T result) { + auto p = SP>(new CPromiseResult()); + p->m_result = result; + return p; + } + + static SP> err(std::string reason) { + auto p = SP>(new CPromiseResult()); + p->m_error = reason; + p->m_hasError = true; + return p; + } + + T m_result = {}; + std::string m_error = {}; + bool m_hasError = false; + + friend class CPromiseResolver; +}; + +template +class CPromise { + public: + CPromise(const CPromise&) = delete; + CPromise(CPromise&&) = delete; + CPromise& operator=(const CPromise&) = delete; + CPromise& operator=(CPromise&&) = delete; + + static SP make(const std::function>)>& fn) { + auto sp = SP>(new CPromise()); + fn(SP>(new CPromiseResolver(sp))); + return sp; + } + + void then(std::function>)>&& fn) { + m_then = std::move(fn); + if (m_result) + m_then(m_result); + } + + private: + CPromise() = default; + + const std::function>)> m_fn; + std::function>)> m_then; + SP> m_result; + + friend class CPromiseResult; + friend class CPromiseResolver; +}; \ No newline at end of file -- cgit v1.2.3