Files
btrfs-sync-backend/Src/Asio.hpp
2025-10-06 08:22:54 +06:00

212 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
});
}
};
}