ADC/net/adc_netserver.h
Timur A. Fatkhullin afd1a917b4 ...
2024-11-09 03:15:59 +03:00

379 lines
11 KiB
C++

#pragma once
/*
ABSTRACT DEVICE COMPONENTS LIBRARY
*/
#include <filesystem>
#include <functional>
#include <list>
#include <set>
#include <unordered_map>
#if __has_include(<unistd.h>) // POSIX
#define FORK_EXISTS 1
#include <sys/stat.h>
#include <unistd.h>
#include <cerrno>
#endif
#include "adc_net_concepts.h"
namespace adc
{
/* SOME USEFULL PRIVITIVES */
// Ageneric implementation pf POSIX OS daemon
class AdcPosixGenericDaemon
{
public:
virtual ~AdcPosixGenericDaemon() = default;
// run server as daemon (still only on POSIX OSes)
void daemonize()
{
daemonizePrepare();
// reference implementation of forking for POSIX OSes
#ifdef FORK_EXISTS
// get TEMP directory in OS
auto tmp_path = std::filesystem::temp_directory_path();
if (tmp_path.empty()) {
tmp_path = std::filesystem::current_path().root_path();
}
// _ioContext.notify_fork(asio::execution_context::fork_prepare);
if (pid_t pid = fork()) {
if (pid > 0) {
exit(0);
} else {
throw std::system_error(errno, std::generic_category(), "CANNOT FORK 1-STAGE");
}
}
if (setsid() == -1) {
throw std::system_error(errno, std::generic_category(), "CANNOT FORK SETSID");
}
std::filesystem::current_path(tmp_path);
umask(0);
if (pid_t pid = fork()) {
if (pid > 0) {
exit(0);
} else {
throw std::system_error(errno, std::generic_category(), "CANNOT FORK 2-STAGE");
}
}
close(0);
close(1);
close(2);
// _ioContext.notify_fork(asio::io_context::fork_child);
#endif
daemonizeFinalize();
}
protected:
virtual void daemonizePrepare() = 0;
virtual void daemonizeFinalize() = 0;
};
// a basic network session manager (basic start and stop functionality)
class AdcNetSessionManager
{
public:
virtual ~AdcNetSessionManager() = default;
protected:
AdcNetSessionManager() = default;
AdcNetSessionManager(const AdcNetSessionManager&) = delete;
AdcNetSessionManager(AdcNetSessionManager&& other)
{
moveInstFunc(&other, this);
}
AdcNetSessionManager& operator=(const AdcNetSessionManager&) = delete;
AdcNetSessionManager& operator=(AdcNetSessionManager&& other)
{
moveInstFunc(&other, this);
return *this;
};
template <interfaces::adc_netsession_c SessionT>
constexpr static bool anySessionPredicate(const typename SessionT::netsession_ident_t&)
{
return true;
}
// started sessions weak pointers
template <interfaces::adc_netsession_c SessionT>
static inline std::unordered_map<const AdcNetSessionManager*, std::list<std::weak_ptr<SessionT>>> _serverSessions{};
std::vector<std::function<bool(const AdcNetSessionManager*)>> _stopSessionFunc;
std::vector<std::function<void(const AdcNetSessionManager*, const AdcNetSessionManager*)>> _moveCtorFunc;
template <interfaces::adc_netsession_c SessionT>
void startSession(std::shared_ptr<SessionT>& sess_ptr)
{
auto it = _serverSessions<SessionT>[this].end();
it = _serverSessions<SessionT>[this].emplace(it, sess_ptr);
sess_ptr->start();
_stopSessionFunc.emplace_back([it](const AdcNetSessionManager* inst) {
if (!it->expired()) { // session is still existing
auto sess = it->lock();
sess->stop();
_serverSessions<SessionT>[inst].erase(it);
return true;
} else {
return false;
}
});
// define move-function only once per SessionT!
if (_serverSessions<SessionT>[this].size() == 1) {
_moveCtorFunc.emplace_back(
[](const AdcNetSessionManager* new_instance, const AdcNetSessionManager* from_inst) {
_serverSessions<SessionT>[new_instance] = std::move(_serverSessions<SessionT>[from_inst]);
});
}
}
template <interfaces::adc_netsession_c SessionT,
std::predicate<typename SessionT::netsession_ident_t> PredT = decltype(anySessionPredicate<SessionT>)>
size_t stopSessions(PredT&& comp_func = anySessionPredicate<SessionT>())
{
size_t N = 0;
std::set<std::weak_ptr<SessionT>> remove_wptr;
for (auto& wptr : _serverSessions<SessionT>[this]) {
if (std::shared_ptr<SessionT> sptr = wptr.lock()) {
if constexpr (std::same_as<PredT, decltype(anySessionPredicate<SessionT>)>) {
sptr->stop();
remove_wptr.emplace(wptr);
++N;
} else {
if (std::forward<PredT>(comp_func)(sptr->ident())) {
sptr->stop();
remove_wptr.emplace(wptr);
++N;
}
}
} else { // remove already stopped sessions?!!
remove_wptr.emplace(wptr);
}
}
for (auto& wptr : remove_wptr) {
_serverSessions<SessionT>[this].erase(wptr);
}
return N;
}
size_t stopAllSessions()
{
size_t N = 0;
for (auto& func : _stopSessionFunc) {
func(this) ? ++N : 0;
}
_stopSessionFunc.clear();
_moveCtorFunc.clear(); // there are nothing to move after stopping of all sessions
return N;
}
void moveInstFunc(const AdcNetSessionManager* to, const AdcNetSessionManager* from)
{
if (from != to) {
for (auto& func : _moveCtorFunc) {
func(to, from);
}
_stopSessionFunc = std::move(from->_stopSessionFunc);
_moveCtorFunc = std::move(from->_moveCtorFunc);
}
}
};
/* very generic network server */
template <typename IdentT = std::string>
class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager
{
public:
typedef IdentT server_ident_t;
AdcGenericNetServer(const server_ident_t& id) : _serverIdent(id) {}
AdcGenericNetServer(const AdcGenericNetServer&) = delete;
AdcGenericNetServer(AdcGenericNetServer&& other)
: AdcPosixGenericDaemon(std::move(other)), AdcNetSessionManager(std::move(other))
{
if (this == &other) {
return;
}
_serverIdent = std::move(other._serverIdent);
_stopListenFunc = std::move(other._stopListenFunc);
for (auto& func : other._moveCtorFunc) {
func(&other, this);
}
_moveCtorFunc = std::move(other._moveCtorFunc);
}
virtual ~AdcGenericNetServer() = default;
AdcGenericNetServer& operator=(const AdcGenericNetServer&) = delete;
AdcGenericNetServer& operator=(AdcGenericNetServer&& other)
{
if (this != &other) {
AdcPosixGenericDaemon::operator=(std::move(other));
AdcNetSessionManager::operator=(std::move(other));
_serverIdent = std::move(other._serverIdent);
_stopListenFunc = std::move(other._stopListenFunc);
for (auto& func : other._moveCtorFunc) {
func(&other, this);
}
_moveCtorFunc = std::move(other._moveCtorFunc);
}
return *this;
}
virtual server_ident_t ident() const
{
return _serverIdent;
}
// start accepting remote connections, create and start given network session
// It must be assumed that this is asynchronous operation!!!
template <interfaces::adc_netsession_c SessionT, typename... AcceptorCtorArgTs>
void start(SessionT::netsession_ident_t id, SessionT::netsession_ctx_t sess_ctx, AcceptorCtorArgTs&&... ctor_args)
requires traits::adc_hashable_c<typename SessionT::netsession_ident_t>
{
if (!_isListening<SessionT>[this][id]) {
auto acceptor = std::make_shared<typename SessionT::netservice_t::acceptor_t>(
std::forward<AcceptorCtorArgTs>(ctor_args)...);
_stopListenFunc.emplace_back([acceptor, id](const AdcGenericNetServer* inst) {
std::error_code ec;
acceptor->close(ec);
_isListening<SessionT>[inst][id] = false;
});
doAccept<SessionT>(acceptor, std::move(id), std::move(sess_ctx));
}
// only once per SessionT
if (_isListening<SessionT>[this].size() == 1) {
_moveCtorFunc.emplace_back(
[](const AdcGenericNetServer* new_instance, const AdcGenericNetServer* from_inst) {
_isListening<SessionT>[new_instance] = std::move(_isListening<SessionT>[from_inst]);
});
}
};
template <interfaces::adc_netsession_c SessionT>
bool isListening(const typename SessionT::netsession_ident_t& id) const
{
return _isListening<SessionT>[this][id];
}
virtual void start() = 0;
virtual void stop()
{
for (auto& func : _stopListenFunc) {
func(this);
}
_stopListenFunc.clear();
stopAllSessions();
};
protected:
// template <interfaces::adc_netsession_c SessionT>
// inline static std::unordered_map<const AdcGenericNetServer*, bool> _isListening{};
template <interfaces::adc_netsession_c SessionT>
inline static std::unordered_map<const AdcGenericNetServer*,
std::unordered_map<typename SessionT::netsession_ident_t, bool>>
_isListening{};
std::vector<std::function<void(const AdcGenericNetServer*)>> _stopListenFunc;
std::vector<std::function<void(const AdcGenericNetServer*, const AdcGenericNetServer*)>> _moveCtorFunc;
server_ident_t _serverIdent;
template <typename SessionT, typename AT, typename IDT, typename CTXT>
void doAccept(std::shared_ptr<AT> acceptor, IDT id, CTXT sess_ctx)
{
acceptor->asyncAccept([acceptor, id = std::move(id), sess_ctx = std::move(sess_ctx), this](
auto ec, typename SessionT::netservice_t srv) mutable {
if (!ec) {
auto sess = std::make_shared<SessionT>(id, std::move(srv), sess_ctx);
startSession(sess);
_isListening<SessionT>[this][id] = true;
doAccept<SessionT>(acceptor, std::move(id), std::move(sess_ctx));
} else {
errorMessage(SessionT::netservice_t::formatError(ec));
_isListening<SessionT>[this][id] = false;
}
});
}
virtual void errorMessage(const std::string&) = 0;
};
namespace interfaces
{
template <typename T>
concept adc_generic_netserver_c = requires {
typename T::server_ident_t;
requires std::derived_from<T, adc::AdcGenericNetServer<typename T::server_ident_t>>;
};
} // namespace interfaces
} // namespace adc