#pragma once #include #include "libipc/imp/export.h" #include "libipc/def.h" #include "libipc/buffer.h" #include "libipc/shm.h" namespace ipc { using handle_t = void*; using buff_t = buffer; enum : unsigned { sender, receiver }; template struct LIBIPC_EXPORT chan_impl { static ipc::handle_t init_first(); static bool connect (ipc::handle_t * ph, char const * name, unsigned mode); static bool connect (ipc::handle_t * ph, prefix, char const * name, unsigned mode); static bool reconnect (ipc::handle_t * ph, unsigned mode); static void disconnect(ipc::handle_t h); static void destroy (ipc::handle_t h); static char const * name(ipc::handle_t h); // Release memory without waiting for the connection to disconnect. static void release(ipc::handle_t h) noexcept; // Force cleanup of all shared memory storage that handles depend on. static void clear(ipc::handle_t h) noexcept; static void clear_storage(char const * name) noexcept; static void clear_storage(prefix, char const * name) noexcept; static std::size_t recv_count (ipc::handle_t h); static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm); static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm); static buff_t recv(ipc::handle_t h, std::uint64_t tm); static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm); static buff_t try_recv(ipc::handle_t h); }; template class chan_wrapper { private: using detail_t = chan_impl; ipc::handle_t h_ = detail_t::init_first(); unsigned mode_ = ipc::sender; bool connected_ = false; public: chan_wrapper() noexcept = default; explicit chan_wrapper(char const * name, unsigned mode = ipc::sender) : connected_{this->connect(name, mode)} { } chan_wrapper(prefix pref, char const * name, unsigned mode = ipc::sender) : connected_{this->connect(pref, name, mode)} { } chan_wrapper(chan_wrapper&& rhs) noexcept : chan_wrapper{} { swap(rhs); } ~chan_wrapper() { detail_t::destroy(h_); } void swap(chan_wrapper& rhs) noexcept { std::swap(h_ , rhs.h_); std::swap(mode_ , rhs.mode_); std::swap(connected_, rhs.connected_); } chan_wrapper& operator=(chan_wrapper rhs) noexcept { swap(rhs); return *this; } char const * name() const noexcept { return detail_t::name(h_); } // Release memory without waiting for the connection to disconnect. void release() noexcept { detail_t::release(h_); h_ = nullptr; } // Clear shared memory files under opened handle. void clear() noexcept { detail_t::clear(h_); h_ = nullptr; } // Clear shared memory files under a specific name. static void clear_storage(char const * name) noexcept { detail_t::clear_storage(name); } // Clear shared memory files under a specific name with a prefix. static void clear_storage(prefix pref, char const * name) noexcept { detail_t::clear_storage(pref, name); } ipc::handle_t handle() const noexcept { return h_; } bool valid() const noexcept { return (handle() != nullptr); } unsigned mode() const noexcept { return mode_; } chan_wrapper clone() const { return chan_wrapper { name(), mode_ }; } /** * Building handle, then try connecting with name & mode flags. */ bool connect(char const * name, unsigned mode = ipc::sender | ipc::receiver) { if (name == nullptr || name[0] == '\0') return false; detail_t::disconnect(h_); // clear old connection return connected_ = detail_t::connect(&h_, name, mode_ = mode); } bool connect(prefix pref, char const * name, unsigned mode = ipc::sender | ipc::receiver) { if (name == nullptr || name[0] == '\0') return false; detail_t::disconnect(h_); // clear old connection return connected_ = detail_t::connect(&h_, pref, name, mode_ = mode); } /** * Try connecting with new mode flags. */ bool reconnect(unsigned mode) { if (!valid()) return false; if (connected_ && (mode_ == mode)) return true; return connected_ = detail_t::reconnect(&h_, mode_ = mode); } void disconnect() { if (!valid()) return; detail_t::disconnect(h_); connected_ = false; } std::size_t recv_count() const { return detail_t::recv_count(h_); } bool wait_for_recv(std::size_t r_count, std::uint64_t tm = invalid_value) const { return detail_t::wait_for_recv(h_, r_count, tm); } static bool wait_for_recv(char const * name, std::size_t r_count, std::uint64_t tm = invalid_value) { return chan_wrapper(name).wait_for_recv(r_count, tm); } /** * If timeout, this function would call 'force_push' to send the data forcibly. */ bool send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) { return detail_t::send(h_, data, size, tm); } bool send(buff_t const & buff, std::uint64_t tm = default_timeout) { return this->send(buff.data(), buff.size(), tm); } bool send(std::string const & str, std::uint64_t tm = default_timeout) { return this->send(str.c_str(), str.size() + 1, tm); } /** * If timeout, this function would just return false. */ bool try_send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) { return detail_t::try_send(h_, data, size, tm); } bool try_send(buff_t const & buff, std::uint64_t tm = default_timeout) { return this->try_send(buff.data(), buff.size(), tm); } bool try_send(std::string const & str, std::uint64_t tm = default_timeout) { return this->try_send(str.c_str(), str.size() + 1, tm); } buff_t recv(std::uint64_t tm = invalid_value) { return detail_t::recv(h_, tm); } buff_t try_recv() { return detail_t::try_recv(h_); } }; template using chan = chan_wrapper>; /** * \class route * * \note You could use one producer/server/sender for sending messages to a route, * then all the consumers/clients/receivers which are receiving with this route, * would receive your sent messages. * A route could only be used in 1 to N (one producer/writer to multi consumers/readers). */ using route = chan; /** * \class channel * * \note You could use multi producers/writers for sending messages to a channel, * then all the consumers/readers which are receiving with this channel, * would receive your sent messages. */ using channel = chan; } // namespace ipc