Мини релиз
This commit is contained in:
212
Src/Asio.hpp
Normal file
212
Src/Asio.hpp
Normal file
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/experimental/awaitable_operators.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <boost/stacktrace.hpp>
|
||||
|
||||
|
||||
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<typename Result = void, typename Executor = asio::any_io_executor>
|
||||
using coro = asio::awaitable<Result, Executor>;
|
||||
|
||||
/*
|
||||
Асинхроный ожидатель завершения использования ресурса
|
||||
*/
|
||||
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<void()> OnNoUse;
|
||||
std::atomic_int Uses = 0;
|
||||
|
||||
public:
|
||||
AsyncUseControl(asio::io_context &ioc)
|
||||
: IOC(ioc)
|
||||
{}
|
||||
|
||||
template<BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) Token = asio::default_completion_token_t<asio::io_context>>
|
||||
auto wait(Token&& token = asio::default_completion_token_t<asio::io_context>()) {
|
||||
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<Token, void()>(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<IAsyncDestructible> {
|
||||
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<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
|
||||
static std::shared_ptr<T> CreateShared(asio::io_context &ioc, T *ptr)
|
||||
{
|
||||
return std::shared_ptr<T>(ptr, [&ioc](T *ptr) {
|
||||
boost::asio::co_spawn(ioc,
|
||||
[ptr]() mutable -> coro<> {
|
||||
try {
|
||||
co_await dynamic_cast<IAsyncDestructible*>(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<typename T, typename = typename std::is_same<IAsyncDestructible, T>>
|
||||
static coro<std::shared_ptr<T>> CreateShared(T *ptr)
|
||||
{
|
||||
co_return std::shared_ptr<T>(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<IAsyncDestructible*>(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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user