#pragma once #include #include #include #include #include #include using namespace boost::asio::experimental::awaitable_operators; inline std::string tabulate(const std::string& tb) { return boost::algorithm::replace_all_copy(tb, "\n", "\n\t"); } // #ifdef NDEBUG #define MAKE_ERROR(text) { std::stringstream error; error << text << std::endl; throw std::runtime_error(error.str()); } #define MAKE_WARNING(text) { std::stringstream error; error << text << std::endl; std::cerr << error.str(); } // #else // #define MAKE_ERROR(text) { std::stringstream error; error << boost::stacktrace::stacktrace(); std::string tb = tabulate(error.str()); error.str(""); error << text << "\n\t" << tb << std::endl; throw std::runtime_error(error.str()); } // #define MAKE_WARNING(text) { std::stringstream error; error << boost::stacktrace::stacktrace(); std::string tb = tabulate(error.str()); error.str(""); error << text << "\n\t" << tb << std::endl; std::cerr << error.str(); } // #endif #define MAKE_INFO(text) { std::stringstream info; info << text << std::endl; std::cout << info.str(); } namespace Asio { namespace asio = boost::asio; template using coro = asio::awaitable; /* Асинхроный ожидатель завершения использования ресурса */ class AsyncUseControl { public: class Lock { AsyncUseControl *AUC; public: Lock(AsyncUseControl *auc) : AUC(auc) {} Lock() : AUC(nullptr) {} ~Lock() { if(AUC) unlock(); } Lock(const Lock&) = delete; Lock(Lock&& obj) : AUC(obj.AUC) { obj.AUC = nullptr; } Lock& operator=(const Lock&) = delete; Lock& operator=(Lock&& obj) { if(&obj == this) return *this; if(AUC) unlock(); AUC = obj.AUC; obj.AUC = nullptr; return *this; } void unlock() { assert(AUC); if(--AUC->Uses == 0 && AUC->OnNoUse) { asio::post(AUC->IOC, std::move(AUC->OnNoUse)); } AUC = nullptr; } }; private: asio::io_context &IOC; bool NoUse = false; std::move_only_function OnNoUse; std::atomic_int Uses = 0; public: AsyncUseControl(asio::io_context &ioc) : IOC(ioc) {} template> auto wait(Token&& token = asio::default_completion_token_t()) { NoUse = true; auto initiation = [this](auto&& token) { int value; do { value = Uses.exchange(-1); } while(value == -1); OnNoUse = std::move(token); if(value == 0) { asio::post(IOC, std::move(OnNoUse)); } Uses.exchange(value); }; return asio::async_initiate(initiation, token); } Lock use() { int value; do { value = Uses.exchange(-1); } while(value == -1); if(NoUse) { Uses.exchange(value); throw boost::system::system_error(asio::error::operation_aborted, "OnNoUse"); } Uses.exchange(++value); return Lock(this); } }; /* Используется, чтобы вместо уничтожения объекта в умной ссылке, вызвать корутину с co_await asyncDestructor() */ class IAsyncDestructible : public std::enable_shared_from_this { protected: asio::io_context &IOC; AsyncUseControl AUC; virtual coro<> asyncDestructor() { co_await AUC.wait(); } public: IAsyncDestructible(asio::io_context &ioc) : IOC(ioc), AUC(ioc) {} virtual ~IAsyncDestructible() {} protected: template> static std::shared_ptr CreateShared(asio::io_context &ioc, T *ptr) { return std::shared_ptr(ptr, [&ioc](T *ptr) { boost::asio::co_spawn(ioc, [ptr]() mutable -> coro<> { try { co_await dynamic_cast(ptr)->asyncDestructor(); } catch(const std::exception& exc) { std::string error = "IAsyncDestructible<"; error += typeid(T).name(); error += ">:\n"; error += tabulate(exc.what()); std::cerr << error << std::endl; } try { delete ptr; } catch(const std::exception& exc) { std::string error = "IAsyncDestructible~<"; error += typeid(T).name(); error += ">:\n"; error += tabulate(exc.what()); std::cerr << error << std::endl; } }, boost::asio::detached); }); } template> static coro> CreateShared(T *ptr) { co_return std::shared_ptr(ptr, [ioc = asio::get_associated_executor(co_await asio::this_coro::executor)](T *ptr) { boost::asio::co_spawn(ioc, [ptr]() mutable -> coro<> { try { co_await dynamic_cast(ptr)->asyncDestructor(); } catch(const std::exception& exc) { std::string error = "IAsyncDestructible<"; error += typeid(T).name(); error += ">:\n"; std::string subError = exc.what(); boost::replace_all(subError, "\n", "\n\t"); error += subError; std::cerr << error << std::endl; } asio::post(asio::get_associated_executor(co_await asio::this_coro::executor), [ptr](){ delete ptr; }); }, boost::asio::detached); }); } }; }