diff --git a/include/export.h b/include/export.h index ef23d5d..c555574 100644 --- a/include/export.h +++ b/include/export.h @@ -1,52 +1,52 @@ -#pragma once - -#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) - -# define IPC_DECL_EXPORT Q_DECL_EXPORT -# define IPC_DECL_IMPORT Q_DECL_IMPORT - -#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) - -/* - * Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT. - * Not using QtCore cause it shouldn't depend on Qt. -*/ - -#if defined(_MSC_VER) -# define IPC_DECL_EXPORT __declspec(dllexport) -# define IPC_DECL_IMPORT __declspec(dllimport) -#elif defined(__ARMCC__) || defined(__CC_ARM) -# if defined(ANDROID) || defined(__linux__) || defined(__linux) -# define IPC_DECL_EXPORT __attribute__((visibility("default"))) -# define IPC_DECL_IMPORT __attribute__((visibility("default"))) -# else -# define IPC_DECL_EXPORT __declspec(dllexport) -# define IPC_DECL_IMPORT __declspec(dllimport) -# endif -#elif defined(__GNUC__) -# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ - defined(WIN64) || defined(_WIN64) || defined(__WIN64__) -# define IPC_DECL_EXPORT __declspec(dllexport) -# define IPC_DECL_IMPORT __declspec(dllimport) -# else -# define IPC_DECL_EXPORT __attribute__((visibility("default"))) -# define IPC_DECL_IMPORT __attribute__((visibility("default"))) -# endif -#else -# define IPC_DECL_EXPORT __attribute__((visibility("default"))) -# define IPC_DECL_IMPORT __attribute__((visibility("default"))) -#endif - -#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) - -/* - * Define IPC_EXPORT for exporting function & class. -*/ - -#ifndef IPC_EXPORT -#if defined(__IPC_LIBRARY__) -# define IPC_EXPORT IPC_DECL_EXPORT -#else -# define IPC_EXPORT IPC_DECL_IMPORT -#endif -#endif /*IPC_EXPORT*/ +#pragma once + +#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) + +# define IPC_DECL_EXPORT Q_DECL_EXPORT +# define IPC_DECL_IMPORT Q_DECL_IMPORT + +#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) + +/* + * Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT. + * Not using QtCore cause it shouldn't depend on Qt. +*/ + +#if defined(_MSC_VER) +# define IPC_DECL_EXPORT __declspec(dllexport) +# define IPC_DECL_IMPORT __declspec(dllimport) +#elif defined(__ARMCC__) || defined(__CC_ARM) +# if defined(ANDROID) || defined(__linux__) || defined(__linux) +# define IPC_DECL_EXPORT __attribute__((visibility("default"))) +# define IPC_DECL_IMPORT __attribute__((visibility("default"))) +# else +# define IPC_DECL_EXPORT __declspec(dllexport) +# define IPC_DECL_IMPORT __declspec(dllimport) +# endif +#elif defined(__GNUC__) +# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +# define IPC_DECL_EXPORT __declspec(dllexport) +# define IPC_DECL_IMPORT __declspec(dllimport) +# else +# define IPC_DECL_EXPORT __attribute__((visibility("default"))) +# define IPC_DECL_IMPORT __attribute__((visibility("default"))) +# endif +#else +# define IPC_DECL_EXPORT __attribute__((visibility("default"))) +# define IPC_DECL_IMPORT __attribute__((visibility("default"))) +#endif + +#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) + +/* + * Define IPC_EXPORT for exporting function & class. +*/ + +#ifndef IPC_EXPORT +#if defined(__IPC_LIBRARY__) +# define IPC_EXPORT IPC_DECL_EXPORT +#else +# define IPC_EXPORT IPC_DECL_IMPORT +#endif +#endif /*IPC_EXPORT*/ diff --git a/include/ipc.h b/include/ipc.h index f440026..187b01b 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -1,151 +1,151 @@ -#pragma once - -#include "export.h" -#include "def.h" -#include "buffer.h" -#include "shm.h" - -#include - -namespace ipc { - -using handle_t = void*; -using buff_t = buffer; - -enum : unsigned { - sender, - receiver -}; - -template -struct IPC_EXPORT chan_impl { - static handle_t connect (char const * name, unsigned mode); - static void disconnect(handle_t h); - - static char const * name(handle_t h); - - static std::size_t recv_count(handle_t h); - static bool wait_for_recv(handle_t h, std::size_t r_count, std::size_t tm); - - static bool send(handle_t h, void const * data, std::size_t size); - static buff_t recv(handle_t h, std::size_t tm); - - static bool try_send(handle_t h, void const * data, std::size_t size); - static buff_t try_recv(handle_t h); -}; - -template -class chan_wrapper { -private: - using detail_t = chan_impl; - handle_t h_ = nullptr; - -public: - chan_wrapper() = default; - - explicit chan_wrapper(char const * name, unsigned mode = sender) { - this->connect(name, mode); - } - - chan_wrapper(chan_wrapper&& rhs) { - swap(rhs); - } - - ~chan_wrapper() { - disconnect(); - } - - void swap(chan_wrapper& rhs) { - std::swap(h_, rhs.h_); - } - - chan_wrapper& operator=(chan_wrapper rhs) { - swap(rhs); - return *this; - } - - char const * name() const { - return detail_t::name(h_); - } - - handle_t handle() const { - return h_; - } - - bool valid() const { - return (handle() != nullptr); - } - - chan_wrapper clone() const { - return chan_wrapper { name() }; - } - - bool connect(char const * name, unsigned mode = sender | receiver) { - if (name == nullptr || name[0] == '\0') return false; - this->disconnect(); - h_ = detail_t::connect(name, mode); - return valid(); - } - - void disconnect() { - if (!valid()) return; - detail_t::disconnect(h_); - h_ = nullptr; - } - - std::size_t recv_count() const { - return detail_t::recv_count(h_); - } - - bool wait_for_recv(std::size_t r_count, std::size_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::size_t tm = invalid_value) { - return chan_wrapper(name).wait_for_recv(r_count, tm); - } - - bool send (void const * data, std::size_t size) { return detail_t::send(h_, data, size) ; } - bool send (buff_t const & buff) { return this->send(buff.data(), buff.size()) ; } - bool send (std::string const & str) { return this->send(str.c_str(), str.size() + 1); } - - bool try_send(void const * data, std::size_t size) { return detail_t::try_send(h_, data, size) ; } - bool try_send(buff_t const & buff) { return this->try_send(buff.data(), buff.size()) ; } - bool try_send(std::string const & str) { return this->try_send(str.c_str(), str.size() + 1); } - - buff_t recv(std::size_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 - * - * 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 - * - * 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 +#pragma once + +#include "export.h" +#include "def.h" +#include "buffer.h" +#include "shm.h" + +#include + +namespace ipc { + +using handle_t = void*; +using buff_t = buffer; + +enum : unsigned { + sender, + receiver +}; + +template +struct IPC_EXPORT chan_impl { + static handle_t connect (char const * name, unsigned mode); + static void disconnect(handle_t h); + + static char const * name(handle_t h); + + static std::size_t recv_count(handle_t h); + static bool wait_for_recv(handle_t h, std::size_t r_count, std::size_t tm); + + static bool send(handle_t h, void const * data, std::size_t size); + static buff_t recv(handle_t h, std::size_t tm); + + static bool try_send(handle_t h, void const * data, std::size_t size); + static buff_t try_recv(handle_t h); +}; + +template +class chan_wrapper { +private: + using detail_t = chan_impl; + handle_t h_ = nullptr; + +public: + chan_wrapper() = default; + + explicit chan_wrapper(char const * name, unsigned mode = sender) { + this->connect(name, mode); + } + + chan_wrapper(chan_wrapper&& rhs) { + swap(rhs); + } + + ~chan_wrapper() { + disconnect(); + } + + void swap(chan_wrapper& rhs) { + std::swap(h_, rhs.h_); + } + + chan_wrapper& operator=(chan_wrapper rhs) { + swap(rhs); + return *this; + } + + char const * name() const { + return detail_t::name(h_); + } + + handle_t handle() const { + return h_; + } + + bool valid() const { + return (handle() != nullptr); + } + + chan_wrapper clone() const { + return chan_wrapper { name() }; + } + + bool connect(char const * name, unsigned mode = sender | receiver) { + if (name == nullptr || name[0] == '\0') return false; + this->disconnect(); + h_ = detail_t::connect(name, mode); + return valid(); + } + + void disconnect() { + if (!valid()) return; + detail_t::disconnect(h_); + h_ = nullptr; + } + + std::size_t recv_count() const { + return detail_t::recv_count(h_); + } + + bool wait_for_recv(std::size_t r_count, std::size_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::size_t tm = invalid_value) { + return chan_wrapper(name).wait_for_recv(r_count, tm); + } + + bool send (void const * data, std::size_t size) { return detail_t::send(h_, data, size) ; } + bool send (buff_t const & buff) { return this->send(buff.data(), buff.size()) ; } + bool send (std::string const & str) { return this->send(str.c_str(), str.size() + 1); } + + bool try_send(void const * data, std::size_t size) { return detail_t::try_send(h_, data, size) ; } + bool try_send(buff_t const & buff) { return this->try_send(buff.data(), buff.size()) ; } + bool try_send(std::string const & str) { return this->try_send(str.c_str(), str.size() + 1); } + + buff_t recv(std::size_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 + * + * 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 + * + * 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 diff --git a/include/pool_alloc.h b/include/pool_alloc.h index 8d96237..3509a2d 100644 --- a/include/pool_alloc.h +++ b/include/pool_alloc.h @@ -1,103 +1,103 @@ -#pragma once - -#include -#include - -#include "export.h" -#include "def.h" - -namespace ipc { -namespace mem { - -class IPC_EXPORT pool_alloc { -public: - static void clear(); - static void* alloc(std::size_t size); - static void free(void* p, std::size_t size); -}; - -//////////////////////////////////////////////////////////////// -/// construct/destruct an object -//////////////////////////////////////////////////////////////// - -namespace detail { - -template -struct impl { - template - static T* construct(T* p, P&&... params) { - ::new (p) T(std::forward

(params)...); - return p; - } - - static void destruct(T* p) { - reinterpret_cast(p)->~T(); - } -}; - -template -struct impl { - using type = T[N]; - - template - static type* construct(type* p, P&&... params) { - for (size_t i = 0; i < N; ++i) { - impl::construct(&((*p)[i]), std::forward

(params)...); - } - return p; - } - - static void destruct(type* p) { - for (size_t i = 0; i < N; ++i) { - impl::destruct(&((*p)[i])); - } - } -}; - -} // namespace detail - -template -T* construct(T* p, P&&... params) { - return detail::impl::construct(p, std::forward

(params)...); -} - -template -T* construct(void* p, P&&... params) { - return construct(static_cast(p), std::forward

(params)...); -} - -template -void destruct(T* p) { - return detail::impl::destruct(p); -} - -template -void destruct(void* p) { - destruct(static_cast(p)); -} - -//////////////////////////////////////////////////////////////// -/// general alloc/free -//////////////////////////////////////////////////////////////// - -inline void* alloc(std::size_t size) { - return pool_alloc::alloc(size); -} - -template -T* alloc(P&&... params) { - return construct(pool_alloc::alloc(sizeof(T)), std::forward

(params)...); -} - -inline void free(void* p, std::size_t size) { - pool_alloc::free(p, size); -} - -template -void free(T* p) { - destruct(p); - pool_alloc::free(p, sizeof(T)); -} - -} // namespace mem -} // namespace ipc +#pragma once + +#include +#include + +#include "export.h" +#include "def.h" + +namespace ipc { +namespace mem { + +class IPC_EXPORT pool_alloc { +public: + static void clear(); + static void* alloc(std::size_t size); + static void free(void* p, std::size_t size); +}; + +//////////////////////////////////////////////////////////////// +/// construct/destruct an object +//////////////////////////////////////////////////////////////// + +namespace detail { + +template +struct impl { + template + static T* construct(T* p, P&&... params) { + ::new (p) T(std::forward

(params)...); + return p; + } + + static void destruct(T* p) { + reinterpret_cast(p)->~T(); + } +}; + +template +struct impl { + using type = T[N]; + + template + static type* construct(type* p, P&&... params) { + for (size_t i = 0; i < N; ++i) { + impl::construct(&((*p)[i]), std::forward

(params)...); + } + return p; + } + + static void destruct(type* p) { + for (size_t i = 0; i < N; ++i) { + impl::destruct(&((*p)[i])); + } + } +}; + +} // namespace detail + +template +T* construct(T* p, P&&... params) { + return detail::impl::construct(p, std::forward

(params)...); +} + +template +T* construct(void* p, P&&... params) { + return construct(static_cast(p), std::forward

(params)...); +} + +template +void destruct(T* p) { + return detail::impl::destruct(p); +} + +template +void destruct(void* p) { + destruct(static_cast(p)); +} + +//////////////////////////////////////////////////////////////// +/// general alloc/free +//////////////////////////////////////////////////////////////// + +inline void* alloc(std::size_t size) { + return pool_alloc::alloc(size); +} + +template +T* alloc(P&&... params) { + return construct(pool_alloc::alloc(sizeof(T)), std::forward

(params)...); +} + +inline void free(void* p, std::size_t size) { + pool_alloc::free(p, size); +} + +template +void free(T* p) { + destruct(p); + pool_alloc::free(p, sizeof(T)); +} + +} // namespace mem +} // namespace ipc diff --git a/include/rw_lock.h b/include/rw_lock.h index c52766a..052ce03 100644 --- a/include/rw_lock.h +++ b/include/rw_lock.h @@ -1,170 +1,170 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -//////////////////////////////////////////////////////////////// -/// Gives hint to processor that improves performance of spin-wait loops. -//////////////////////////////////////////////////////////////// - -#pragma push_macro("IPC_LOCK_PAUSE_") -#undef IPC_LOCK_PAUSE_ - -#if defined(_MSC_VER) -#include // YieldProcessor -/* - See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx - Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168 -*/ -# define IPC_LOCK_PAUSE_() YieldProcessor() -#elif defined(__GNUC__) -#if defined(__i386__) || defined(__x86_64__) -/* - See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2 - PAUSE-Spin Loop Hint, 4-57 - http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference -*/ -# define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause") -#elif defined(__ia64__) || defined(__ia64) -/* - See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3 - hint - Performance Hint, 3:145 - http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html -*/ -# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause") -#elif defined(__arm__) -/* - See: ARM Architecture Reference Manuals (YIELD) - http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html -*/ -# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield") -#endif -#endif/*compilers*/ - -#if !defined(IPC_LOCK_PAUSE_) -/* - Just use a compiler fence, prevent compiler from optimizing loop -*/ -# define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst) -#endif/*!defined(IPC_LOCK_PAUSE_)*/ - -//////////////////////////////////////////////////////////////// -/// Yield to other threads -//////////////////////////////////////////////////////////////// - -namespace ipc { - -template -inline void yield(K& k) noexcept { - if (k < 4) { /* Do nothing */ } - else - if (k < 16) { IPC_LOCK_PAUSE_(); } - else { - std::this_thread::yield(); - return; - } - ++k; -} - -template -inline void sleep(K& k, F&& f) { - if (k < static_cast(N)) { - std::this_thread::yield(); - } - else if (std::forward(f)()) { - return; - } - else { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - return; - } - ++k; -} - -template -inline void sleep(K& k) { - sleep(k, [] { return false; }); -} - -} // namespace ipc - -#pragma pop_macro("IPC_LOCK_PAUSE_") - -namespace ipc { - -class spin_lock { - std::atomic lc_ { 0 }; - -public: - void lock(void) noexcept { - for (unsigned k = 0; - lc_.exchange(1, std::memory_order_acquire); - yield(k)) ; - } - - void unlock(void) noexcept { - lc_.store(0, std::memory_order_release); - } -}; - -class rw_lock { - using lc_ui_t = unsigned; - - std::atomic lc_ { 0 }; - - enum : lc_ui_t { - w_mask = (std::numeric_limits>::max)(), // b 0111 1111 - w_flag = w_mask + 1 // b 1000 0000 - }; - -public: - rw_lock() = default; - - rw_lock(const rw_lock&) = delete; - rw_lock& operator=(const rw_lock&) = delete; - rw_lock(rw_lock&&) = delete; - rw_lock& operator=(rw_lock&&) = delete; - - void lock() noexcept { - for (unsigned k = 0;;) { - auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel); - if (!old) return; // got w-lock - if (!(old & w_flag)) break; // other thread having r-lock - yield(k); // other thread having w-lock - } - // wait for reading finished - for (unsigned k = 0; - lc_.load(std::memory_order_acquire) & w_mask; - yield(k)) ; - } - - void unlock() noexcept { - lc_.store(0, std::memory_order_release); - } - - void lock_shared() noexcept { - auto old = lc_.load(std::memory_order_acquire); - for (unsigned k = 0;;) { - // if w_flag set, just continue - if (old & w_flag) { - yield(k); - old = lc_.load(std::memory_order_acquire); - } - // otherwise try cas lc + 1 (set r-lock) - else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) { - return; - } - // set r-lock failed, old has been updated - } - } - - void unlock_shared() noexcept { - lc_.fetch_sub(1, std::memory_order_release); - } -}; - -} // namespace ipc +#pragma once + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////// +/// Gives hint to processor that improves performance of spin-wait loops. +//////////////////////////////////////////////////////////////// + +#pragma push_macro("IPC_LOCK_PAUSE_") +#undef IPC_LOCK_PAUSE_ + +#if defined(_MSC_VER) +#include // YieldProcessor +/* + See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx + Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168 +*/ +# define IPC_LOCK_PAUSE_() YieldProcessor() +#elif defined(__GNUC__) +#if defined(__i386__) || defined(__x86_64__) +/* + See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2 + PAUSE-Spin Loop Hint, 4-57 + http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference +*/ +# define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause") +#elif defined(__ia64__) || defined(__ia64) +/* + See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3 + hint - Performance Hint, 3:145 + http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html +*/ +# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause") +#elif defined(__arm__) +/* + See: ARM Architecture Reference Manuals (YIELD) + http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html +*/ +# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield") +#endif +#endif/*compilers*/ + +#if !defined(IPC_LOCK_PAUSE_) +/* + Just use a compiler fence, prevent compiler from optimizing loop +*/ +# define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst) +#endif/*!defined(IPC_LOCK_PAUSE_)*/ + +//////////////////////////////////////////////////////////////// +/// Yield to other threads +//////////////////////////////////////////////////////////////// + +namespace ipc { + +template +inline void yield(K& k) noexcept { + if (k < 4) { /* Do nothing */ } + else + if (k < 16) { IPC_LOCK_PAUSE_(); } + else { + std::this_thread::yield(); + return; + } + ++k; +} + +template +inline void sleep(K& k, F&& f) { + if (k < static_cast(N)) { + std::this_thread::yield(); + } + else if (std::forward(f)()) { + return; + } + else { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + return; + } + ++k; +} + +template +inline void sleep(K& k) { + sleep(k, [] { return false; }); +} + +} // namespace ipc + +#pragma pop_macro("IPC_LOCK_PAUSE_") + +namespace ipc { + +class spin_lock { + std::atomic lc_ { 0 }; + +public: + void lock(void) noexcept { + for (unsigned k = 0; + lc_.exchange(1, std::memory_order_acquire); + yield(k)) ; + } + + void unlock(void) noexcept { + lc_.store(0, std::memory_order_release); + } +}; + +class rw_lock { + using lc_ui_t = unsigned; + + std::atomic lc_ { 0 }; + + enum : lc_ui_t { + w_mask = (std::numeric_limits>::max)(), // b 0111 1111 + w_flag = w_mask + 1 // b 1000 0000 + }; + +public: + rw_lock() = default; + + rw_lock(const rw_lock&) = delete; + rw_lock& operator=(const rw_lock&) = delete; + rw_lock(rw_lock&&) = delete; + rw_lock& operator=(rw_lock&&) = delete; + + void lock() noexcept { + for (unsigned k = 0;;) { + auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel); + if (!old) return; // got w-lock + if (!(old & w_flag)) break; // other thread having r-lock + yield(k); // other thread having w-lock + } + // wait for reading finished + for (unsigned k = 0; + lc_.load(std::memory_order_acquire) & w_mask; + yield(k)) ; + } + + void unlock() noexcept { + lc_.store(0, std::memory_order_release); + } + + void lock_shared() noexcept { + auto old = lc_.load(std::memory_order_acquire); + for (unsigned k = 0;;) { + // if w_flag set, just continue + if (old & w_flag) { + yield(k); + old = lc_.load(std::memory_order_acquire); + } + // otherwise try cas lc + 1 (set r-lock) + else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) { + return; + } + // set r-lock failed, old has been updated + } + } + + void unlock_shared() noexcept { + lc_.fetch_sub(1, std::memory_order_release); + } +}; + +} // namespace ipc diff --git a/include/shm.h b/include/shm.h index 8a8ae7c..8b73837 100644 --- a/include/shm.h +++ b/include/shm.h @@ -1,52 +1,52 @@ -#pragma once - -#include - -#include "export.h" - -namespace ipc { -namespace shm { - -using id_t = void*; - -enum : unsigned { - create = 0x01, - open = 0x02 -}; - -IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open); -IPC_EXPORT void * get_mem(id_t id, std::size_t * size); -IPC_EXPORT void release(id_t id); -IPC_EXPORT void remove (id_t id); -IPC_EXPORT void remove (char const * name); - -class IPC_EXPORT handle { -public: - handle(); - handle(char const * name, std::size_t size, unsigned mode = create | open); - handle(handle&& rhs); - - ~handle(); - - void swap(handle& rhs); - handle& operator=(handle rhs); - - bool valid() const; - std::size_t size () const; - char const * name () const; - - bool acquire(char const * name, std::size_t size, unsigned mode = create | open); - void release(); - - void* get() const; - - void attach(id_t); - id_t detach(); - -private: - class handle_; - handle_* p_; -}; - -} // namespace shm -} // namespace ipc +#pragma once + +#include + +#include "export.h" + +namespace ipc { +namespace shm { + +using id_t = void*; + +enum : unsigned { + create = 0x01, + open = 0x02 +}; + +IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open); +IPC_EXPORT void * get_mem(id_t id, std::size_t * size); +IPC_EXPORT void release(id_t id); +IPC_EXPORT void remove (id_t id); +IPC_EXPORT void remove (char const * name); + +class IPC_EXPORT handle { +public: + handle(); + handle(char const * name, std::size_t size, unsigned mode = create | open); + handle(handle&& rhs); + + ~handle(); + + void swap(handle& rhs); + handle& operator=(handle rhs); + + bool valid() const; + std::size_t size () const; + char const * name () const; + + bool acquire(char const * name, std::size_t size, unsigned mode = create | open); + void release(); + + void* get() const; + + void attach(id_t); + id_t detach(); + +private: + class handle_; + handle_* p_; +}; + +} // namespace shm +} // namespace ipc diff --git a/include/tls_pointer.h b/include/tls_pointer.h index f1cb668..e345e57 100644 --- a/include/tls_pointer.h +++ b/include/tls_pointer.h @@ -1,86 +1,90 @@ -#pragma once - -#include -#include -#include - -#include "export.h" - -namespace ipc { -namespace tls { - -using key_t = std::uint64_t; -using destructor_t = void (*)(void*); - -enum : key_t { - invalid_value = (std::numeric_limits::max)() -}; - -IPC_EXPORT key_t create (destructor_t destructor = nullptr); -IPC_EXPORT void release(key_t key); - -IPC_EXPORT bool set(key_t key, void* ptr); -IPC_EXPORT void* get(key_t key); - -//////////////////////////////////////////////////////////////// -/// Thread-local pointer -//////////////////////////////////////////////////////////////// - -/* - - - 1. In Windows, if you do not compile thread_local_ptr.cpp, - use thread_local_ptr will cause memory leaks. - - 2. You need to set the thread_local_ptr's storage manually: - ``` - thread_local_ptr p; - if (!p) p = new int(123); - ``` - Just like an ordinary pointer. Or you could just call create: - ``` - thread_local_ptr p; - p.create(123); - ``` -*/ - -template -class pointer { - key_t key_; - -public: - using value_type = T; - - pointer() { - key_ = tls::create([](void* p) { delete static_cast(p); }); - } - - ~pointer() { - tls::release(key_); - } - - template - T* create(P&&... params) { - thread_local auto ptr = static_cast(*this); - if (ptr == nullptr) { - return ptr = (*this) = new T(std::forward

(params)...); - } - return ptr; - } - - T* operator=(T* ptr) { - set(key_, ptr); - return ptr; - } - - operator T*() const { return static_cast(get(key_)); } - - T& operator* () { return *static_cast(*this); } - const T& operator* () const { return *static_cast(*this); } - - T* operator->() { return static_cast(*this); } - const T* operator->() const { return static_cast(*this); } -}; - -} // namespace tls -} // namespace ipc +#pragma once + +#include +#include +#include + +#include "export.h" + +namespace ipc { +namespace tls { + +using key_t = std::uint64_t; +using destructor_t = void (*)(void*); + +enum : key_t { + invalid_value = (std::numeric_limits::max)() +}; + +IPC_EXPORT key_t create (destructor_t destructor = nullptr); +IPC_EXPORT void release(key_t key); + +IPC_EXPORT bool set(key_t key, void* ptr); +IPC_EXPORT void* get(key_t key); + +//////////////////////////////////////////////////////////////// +/// Thread-local pointer +//////////////////////////////////////////////////////////////// + +/* + * + * + * 1. In Windows, if you do not compile thread_local_ptr.cpp, + * use thread_local_ptr will cause memory leaks. + * + * 2. You need to set the thread_local_ptr's storage manually: + * ``` + * tls::pointer p; + * if (!p) p = new int(123); + * ``` + * Just like an ordinary pointer. Or you could just call create: + * ``` + * tls::pointer p; + * p.create(123); + * ``` +*/ + +template +class pointer { + key_t key_; + +public: + using value_type = T; + + pointer() + : key_(tls::create([](void* p) { delete static_cast(p); })) { + } + + ~pointer() { + tls::release(key_); + } + + template + T* create(P&&... params) { + thread_local auto ptr = static_cast(get(key_)); + if (ptr == nullptr) { + ptr = new T(std::forward

(params)...); + if (!set(key_, ptr)) { + delete ptr; + return nullptr; + } + } + return ptr; + } + + T* operator=(T* ptr) { + set(key_, ptr); + return ptr; + } + + operator T*() const { return static_cast(get(key_)); } + + T& operator* () { return *static_cast(*this); } + const T& operator* () const { return *static_cast(*this); } + + T* operator->() { return static_cast(*this); } + const T* operator->() const { return static_cast(*this); } +}; + +} // namespace tls +} // namespace ipc diff --git a/include/waiter.h b/include/waiter.h index 74dceef..7627373 100644 --- a/include/waiter.h +++ b/include/waiter.h @@ -1,93 +1,93 @@ -#pragma once - -#include "export.h" -#include "def.h" - -namespace ipc { - -class condition; -class IPC_EXPORT mutex { -public: - mutex(); - explicit mutex(char const * name); - mutex(mutex&& rhs); - - ~mutex(); - - static void remove(char const * name); - - void swap(mutex& rhs); - mutex& operator=(mutex rhs); - - bool valid() const; - char const * name () const; - - bool open (char const * name); - void close(); - - bool lock (); - bool unlock(); - -private: - class mutex_; - mutex_* p_; - - friend class condition; -}; - -class IPC_EXPORT semaphore { -public: - semaphore(); - explicit semaphore(char const * name); - semaphore(semaphore&& rhs); - - ~semaphore(); - - static void remove(char const * name); - - void swap(semaphore& rhs); - semaphore& operator=(semaphore rhs); - - bool valid() const; - char const * name () const; - - bool open (char const * name, long count = 0); - void close(); - - bool wait(std::size_t tm = invalid_value); - bool post(long count = 1); - -private: - class semaphore_; - semaphore_* p_; -}; - -class IPC_EXPORT condition { -public: - condition(); - explicit condition(char const * name); - condition(condition&& rhs); - - ~condition(); - - static void remove(char const * name); - - void swap(condition& rhs); - condition& operator=(condition rhs); - - bool valid() const; - char const * name () const; - - bool open (char const * name); - void close(); - - bool wait(mutex&, std::size_t tm = invalid_value); - bool notify(); - bool broadcast(); - -private: - class condition_; - condition_* p_; -}; - -} // namespace ipc +#pragma once + +#include "export.h" +#include "def.h" + +namespace ipc { + +class condition; +class IPC_EXPORT mutex { +public: + mutex(); + explicit mutex(char const * name); + mutex(mutex&& rhs); + + ~mutex(); + + static void remove(char const * name); + + void swap(mutex& rhs); + mutex& operator=(mutex rhs); + + bool valid() const; + char const * name () const; + + bool open (char const * name); + void close(); + + bool lock (); + bool unlock(); + +private: + class mutex_; + mutex_* p_; + + friend class condition; +}; + +class IPC_EXPORT semaphore { +public: + semaphore(); + explicit semaphore(char const * name); + semaphore(semaphore&& rhs); + + ~semaphore(); + + static void remove(char const * name); + + void swap(semaphore& rhs); + semaphore& operator=(semaphore rhs); + + bool valid() const; + char const * name () const; + + bool open (char const * name, long count = 0); + void close(); + + bool wait(std::size_t tm = invalid_value); + bool post(long count = 1); + +private: + class semaphore_; + semaphore_* p_; +}; + +class IPC_EXPORT condition { +public: + condition(); + explicit condition(char const * name); + condition(condition&& rhs); + + ~condition(); + + static void remove(char const * name); + + void swap(condition& rhs); + condition& operator=(condition rhs); + + bool valid() const; + char const * name () const; + + bool open (char const * name); + void close(); + + bool wait(mutex&, std::size_t tm = invalid_value); + bool notify(); + bool broadcast(); + +private: + class condition_; + condition_* p_; +}; + +} // namespace ipc diff --git a/src/buffer.cpp b/src/buffer.cpp index 6607146..b014d6b 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -1,83 +1,83 @@ -#include "buffer.h" -#include "pimpl.h" - -#include - -namespace ipc { - -bool operator==(buffer const & b1, buffer const & b2) { - return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0); -} - -class buffer::buffer_ : public pimpl { -public: - void* p_; - std::size_t s_; - void* a_; - buffer::destructor_t d_; - - buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a) - : p_(p), s_(s), a_(a), d_(d) { - } - - ~buffer_() { - if (d_ == nullptr) return; - d_((a_ == nullptr) ? p_ : a_, s_); - } -}; - -buffer::buffer() - : buffer(nullptr, 0, nullptr, nullptr) { -} - -buffer::buffer(void* p, std::size_t s, destructor_t d) - : p_(p_->make(p, s, d, nullptr)) { -} - -buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional) - : p_(p_->make(p, s, d, additional)) { -} - -buffer::buffer(void* p, std::size_t s) - : buffer(p, s, nullptr) { -} - -buffer::buffer(char const & c) - : buffer(const_cast(&c), 1) { -} - -buffer::buffer(buffer&& rhs) - : buffer() { - swap(rhs); -} - -buffer::~buffer() { - p_->clear(); -} - -void buffer::swap(buffer& rhs) { - std::swap(p_, rhs.p_); -} - -buffer& buffer::operator=(buffer rhs) { - swap(rhs); - return *this; -} - -bool buffer::empty() const noexcept { - return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0); -} - -void* buffer::data() noexcept { - return impl(p_)->p_; -} - -void const * buffer::data() const noexcept { - return impl(p_)->p_; -} - -std::size_t buffer::size() const noexcept { - return impl(p_)->s_; -} - -} // namespace ipc +#include "buffer.h" +#include "pimpl.h" + +#include + +namespace ipc { + +bool operator==(buffer const & b1, buffer const & b2) { + return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0); +} + +class buffer::buffer_ : public pimpl { +public: + void* p_; + std::size_t s_; + void* a_; + buffer::destructor_t d_; + + buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a) + : p_(p), s_(s), a_(a), d_(d) { + } + + ~buffer_() { + if (d_ == nullptr) return; + d_((a_ == nullptr) ? p_ : a_, s_); + } +}; + +buffer::buffer() + : buffer(nullptr, 0, nullptr, nullptr) { +} + +buffer::buffer(void* p, std::size_t s, destructor_t d) + : p_(p_->make(p, s, d, nullptr)) { +} + +buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional) + : p_(p_->make(p, s, d, additional)) { +} + +buffer::buffer(void* p, std::size_t s) + : buffer(p, s, nullptr) { +} + +buffer::buffer(char const & c) + : buffer(const_cast(&c), 1) { +} + +buffer::buffer(buffer&& rhs) + : buffer() { + swap(rhs); +} + +buffer::~buffer() { + p_->clear(); +} + +void buffer::swap(buffer& rhs) { + std::swap(p_, rhs.p_); +} + +buffer& buffer::operator=(buffer rhs) { + swap(rhs); + return *this; +} + +bool buffer::empty() const noexcept { + return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0); +} + +void* buffer::data() noexcept { + return impl(p_)->p_; +} + +void const * buffer::data() const noexcept { + return impl(p_)->p_; +} + +std::size_t buffer::size() const noexcept { + return impl(p_)->s_; +} + +} // namespace ipc diff --git a/src/circ/elem_array.h b/src/circ/elem_array.h index 27ace56..94f3bc5 100644 --- a/src/circ/elem_array.h +++ b/src/circ/elem_array.h @@ -1,60 +1,60 @@ -#pragma once - -#include -#include -#include - -#include "def.h" - -#include "circ/elem_def.h" -#include "platform/detail.h" - -namespace ipc { -namespace circ { - -template -class elem_array : public ipc::circ::conn_head { -public: - using base_t = ipc::circ::conn_head; - using policy_t = Policy; - using cursor_t = decltype(std::declval().cursor()); - using elem_t = typename policy_t::template elem_t; - - enum : std::size_t { - head_size = sizeof(base_t) + sizeof(policy_t), - data_size = DataSize, - elem_max = (std::numeric_limits>::max)() + 1, // default is 255 + 1 - elem_size = sizeof(elem_t), - block_size = elem_size * elem_max - }; - -private: - policy_t head_; - elem_t block_[elem_max] {}; - -public: - cursor_t cursor() const noexcept { - return head_.cursor(); - } - - template - bool push(F&& f) { - return head_.push(this, std::forward(f), block_); - } - - template - bool force_push(F&& f) { - return head_.force_push(this, std::forward(f), block_); - } - - template - bool pop(cursor_t* cur, F&& f) { - if (cur == nullptr) return false; - return head_.pop(this, *cur, std::forward(f), block_); - } -}; - -} // namespace circ -} // namespace ipc +#pragma once + +#include +#include +#include + +#include "def.h" + +#include "circ/elem_def.h" +#include "platform/detail.h" + +namespace ipc { +namespace circ { + +template +class elem_array : public ipc::circ::conn_head { +public: + using base_t = ipc::circ::conn_head; + using policy_t = Policy; + using cursor_t = decltype(std::declval().cursor()); + using elem_t = typename policy_t::template elem_t; + + enum : std::size_t { + head_size = sizeof(base_t) + sizeof(policy_t), + data_size = DataSize, + elem_max = (std::numeric_limits>::max)() + 1, // default is 255 + 1 + elem_size = sizeof(elem_t), + block_size = elem_size * elem_max + }; + +private: + policy_t head_; + elem_t block_[elem_max] {}; + +public: + cursor_t cursor() const noexcept { + return head_.cursor(); + } + + template + bool push(F&& f) { + return head_.push(this, std::forward(f), block_); + } + + template + bool force_push(F&& f) { + return head_.force_push(this, std::forward(f), block_); + } + + template + bool pop(cursor_t* cur, F&& f) { + if (cur == nullptr) return false; + return head_.pop(this, *cur, std::forward(f), block_); + } +}; + +} // namespace circ +} // namespace ipc diff --git a/src/circ/elem_def.h b/src/circ/elem_def.h index c0ea326..d19e5d9 100644 --- a/src/circ/elem_def.h +++ b/src/circ/elem_def.h @@ -1,75 +1,75 @@ -#pragma once - -#include -#include -#include -#include - -#include "rw_lock.h" - -#include "platform/detail.h" - -namespace ipc { -namespace circ { - -using u1_t = ipc::uint_t<8>; -using u2_t = ipc::uint_t<32>; - -constexpr u1_t index_of(u2_t c) noexcept { - return static_cast(c); -} - -class conn_head { - std::atomic cc_ { 0 }; // connection counter - - ipc::spin_lock lc_; - std::atomic constructed_; - - std::atomic dis_flag_; - -public: - void init() { - /* DCLP */ - if (!constructed_.load(std::memory_order_acquire)) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_); - if (!constructed_.load(std::memory_order_relaxed)) { - ::new (this) conn_head; - constructed_.store(true, std::memory_order_release); - } - } - } - - conn_head() = default; - conn_head(const conn_head&) = delete; - conn_head& operator=(const conn_head&) = delete; - - std::size_t connect() noexcept { - return cc_.fetch_add(1, std::memory_order_acq_rel); - } - - std::size_t disconnect() noexcept { - return cc_.fetch_sub(1, std::memory_order_acq_rel); - } - - void try_disconnect() noexcept { - if (!dis_flag_.load(std::memory_order_acquire)) { - cc_.fetch_sub(1, std::memory_order_relaxed); - dis_flag_.store(true, std::memory_order_release); - } - } - - void clear_dis_flag(std::memory_order order = std::memory_order_release) noexcept { - dis_flag_.store(false, order); - } - - bool dis_flag(std::memory_order order = std::memory_order_acquire) const noexcept { - return dis_flag_.load(order); - } - - std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept { - return cc_.load(order); - } -}; - -} // namespace circ -} // namespace ipc +#pragma once + +#include +#include +#include +#include + +#include "rw_lock.h" + +#include "platform/detail.h" + +namespace ipc { +namespace circ { + +using u1_t = ipc::uint_t<8>; +using u2_t = ipc::uint_t<32>; + +constexpr u1_t index_of(u2_t c) noexcept { + return static_cast(c); +} + +class conn_head { + std::atomic cc_ { 0 }; // connection counter + + ipc::spin_lock lc_; + std::atomic constructed_; + + std::atomic dis_flag_; + +public: + void init() { + /* DCLP */ + if (!constructed_.load(std::memory_order_acquire)) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_); + if (!constructed_.load(std::memory_order_relaxed)) { + ::new (this) conn_head; + constructed_.store(true, std::memory_order_release); + } + } + } + + conn_head() = default; + conn_head(const conn_head&) = delete; + conn_head& operator=(const conn_head&) = delete; + + std::size_t connect() noexcept { + return cc_.fetch_add(1, std::memory_order_acq_rel); + } + + std::size_t disconnect() noexcept { + return cc_.fetch_sub(1, std::memory_order_acq_rel); + } + + void try_disconnect() noexcept { + if (!dis_flag_.load(std::memory_order_acquire)) { + cc_.fetch_sub(1, std::memory_order_relaxed); + dis_flag_.store(true, std::memory_order_release); + } + } + + void clear_dis_flag(std::memory_order order = std::memory_order_release) noexcept { + dis_flag_.store(false, order); + } + + bool dis_flag(std::memory_order order = std::memory_order_acquire) const noexcept { + return dis_flag_.load(order); + } + + std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept { + return cc_.load(order); + } +}; + +} // namespace circ +} // namespace ipc diff --git a/src/concept.h b/src/concept.h index 649c25b..4905925 100644 --- a/src/concept.h +++ b/src/concept.h @@ -1,29 +1,29 @@ -#pragma once - -#include - -namespace ipc { - -// concept helpers - -template -using require = std::enable_if_t; - -#ifdef IPC_CONCEPT_ -# error "IPC_CONCEPT_ has been defined." -#endif - -#define IPC_CONCEPT_(NAME, WHAT) \ -template \ -class NAME { \ -private: \ - template \ - static std::true_type check(decltype(std::declval().WHAT)*); \ - template \ - static std::false_type check(...); \ -public: \ - using type = decltype(check(nullptr)); \ - constexpr static auto value = type::value; \ -} - -} // namespace ipc +#pragma once + +#include + +namespace ipc { + +// concept helpers + +template +using require = std::enable_if_t; + +#ifdef IPC_CONCEPT_ +# error "IPC_CONCEPT_ has been defined." +#endif + +#define IPC_CONCEPT_(NAME, WHAT) \ +template \ +class NAME { \ +private: \ + template \ + static std::true_type check(decltype(std::declval().WHAT)*); \ + template \ + static std::false_type check(...); \ +public: \ + using type = decltype(check(nullptr)); \ + constexpr static auto value = type::value; \ +} + +} // namespace ipc diff --git a/src/ipc.cpp b/src/ipc.cpp index 3a8da8b..ca549e4 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -1,485 +1,485 @@ -#include "ipc.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "def.h" -#include "shm.h" -#include "tls_pointer.h" -#include "pool_alloc.h" -#include "queue.h" -#include "policy.h" -#include "rw_lock.h" -#include "log.h" - -#include "memory/resource.h" - -#include "platform/detail.h" -#include "platform/waiter_wrapper.h" - -#include "circ/elem_array.h" - -namespace { - -using namespace ipc; -using msg_id_t = std::size_t; - -template -struct msg_t; - -template -struct msg_t<0, AlignSize> { - msg_id_t conn_; - msg_id_t id_; - int remain_; - bool storage_; -}; - -template -struct msg_t : msg_t<0, AlignSize> { - std::aligned_storage_t data_ {}; - - msg_t() = default; - msg_t(msg_id_t c, msg_id_t i, int r, void const * d, std::size_t s) - : msg_t<0, AlignSize> { c, i, r, (d == nullptr) || (s == 0) } { - if (!this->storage_) { - std::memcpy(&data_, d, s); - } - } -}; - -template -buff_t make_cache(T& data, std::size_t size) { - auto ptr = mem::alloc(size); - std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size)); - return { ptr, size, mem::free }; -} - -struct cache_t { - std::size_t fill_; - buff_t buff_; - - cache_t(std::size_t f, buff_t&& b) - : fill_(f), buff_(std::move(b)) - {} - - void append(void const * data, std::size_t size) { - if (fill_ >= buff_.size() || data == nullptr || size == 0) return; - auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size()); - std::memcpy(static_cast(buff_.data()) + fill_, data, new_fill - fill_); - fill_ = new_fill; - } -}; - -struct conn_info_head { - using acc_t = std::atomic; - - static auto cc_acc() { - static shm::handle acc_h("__CA_CONN__", sizeof(acc_t)); - return static_cast(acc_h.get()); - } - - ipc::string name_; - msg_id_t cc_id_; // connection-info id - waiter cc_waiter_, wt_waiter_, rd_waiter_; - shm::handle acc_h_; - - /* - * thread_local may have some bugs. - * - * - * - https://sourceforge.net/p/mingw-w64/bugs/727/ - * - https://sourceforge.net/p/mingw-w64/bugs/527/ - * - https://github.com/Alexpux/MINGW-packages/issues/2519 - * - https://github.com/ChaiScript/ChaiScript/issues/402 - * - https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html - * - https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827 - */ - tls::pointer> recv_cache_; - - struct simple_push { - - template - using elem_t = shm::id_t; - - circ::u2_t wt_; // write index - - constexpr circ::u2_t cursor() const noexcept { - return 0; - } - - template - bool push(W* /*wrapper*/, F&& f, E* elems) { - std::forward(f)(&(elems[circ::index_of(wt_)])); - ++ wt_; - return true; - } - }; - - circ::elem_array msg_datas_; - - conn_info_head(char const * name) - : name_ (name) - , cc_id_ ((cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed)) - , cc_waiter_(("__CC_CONN__" + name_).c_str()) - , wt_waiter_(("__WT_CONN__" + name_).c_str()) - , rd_waiter_(("__RD_CONN__" + name_).c_str()) - , acc_h_ (("__AC_CONN__" + name_).c_str(), sizeof(acc_t)) { - } - - auto acc() { - return static_cast(acc_h_.get()); - } - - auto& recv_cache() { - return *recv_cache_.create(); - } - - shm::id_t apply_storage(msg_id_t msg_id, std::size_t size) { - return shm::acquire( - ("__ST_CONN__" + ipc::to_string(cc_id_) + - "__" + ipc::to_string(msg_id)).c_str(), size, shm::create); - } - - static shm::id_t acquire_storage(msg_id_t cc_id, msg_id_t msg_id) { - return shm::acquire( - ("__ST_CONN__" + ipc::to_string(cc_id) + - "__" + ipc::to_string(msg_id)).c_str(), 0, shm::open); - } - - void store(shm::id_t dat) { - msg_datas_.push([dat](shm::id_t * id) { - (*id) = dat; - }); - } - - void clear_store() { - msg_datas_.push([](shm::id_t * id) { - if (*id == nullptr) return; - shm::remove(*id); - (*id) = nullptr; - }); - } -}; - -template -bool wait_for(W& waiter, F&& pred, std::size_t tm) { - if (tm == 0) return !pred(); - for (unsigned k = 0; pred();) { - bool loop = true, ret = true; - ipc::sleep(k, [&k, &loop, &ret, &waiter, &pred, tm] { - ret = waiter.wait_if([&loop, &pred] { - return loop = pred(); - }, tm); - k = 0; - return true; - }); - if (!ret ) return false; // timeout or fail - if (!loop) break; - } - return true; -} - -template -struct queue_generator { - - using queue_t = ipc::queue, Policy>; - - struct conn_info_t : conn_info_head { - queue_t que_; - - conn_info_t(char const * name) - : conn_info_head(name) - , que_(("__QU_CONN__" + - ipc::to_string(DataSize) + "__" + - ipc::to_string(AlignSize) + "__" + name).c_str()) { - } - }; -}; - -template -struct detail_impl { - -using queue_t = typename queue_generator::queue_t; -using conn_info_t = typename queue_generator::conn_info_t; - -constexpr static conn_info_t* info_of(ipc::handle_t h) { - return static_cast(h); -} - -constexpr static queue_t* queue_of(ipc::handle_t h) { - return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_); -} - -/* API implementations */ - -static ipc::handle_t connect(char const * name, bool start) { - auto h = mem::alloc(name); - auto que = queue_of(h); - if (que == nullptr) { - return nullptr; - } - if (start) { - if (que->connect()) { // wouldn't connect twice - info_of(h)->cc_waiter_.broadcast(); - } - } - return h; -} - -static void disconnect(ipc::handle_t h) { - auto que = queue_of(h); - if (que == nullptr) { - return; - } - if (que->disconnect()) { - info_of(h)->cc_waiter_.broadcast(); - } - mem::free(info_of(h)); -} - -static std::size_t recv_count(ipc::handle_t h) { - auto que = queue_of(h); - if (que == nullptr) { - return invalid_value; - } - return que->conn_count(); -} - -static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { - auto que = queue_of(h); - if (que == nullptr) { - return false; - } - return wait_for(info_of(h)->cc_waiter_, [que, r_count] { - return que->conn_count() < r_count; - }, tm); -} - -template -static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) { - if (data == nullptr || size == 0) { - ipc::error("fail: send(%p, %zd)\n", data, size); - return false; - } - auto que = queue_of(h); - if (que == nullptr) { - ipc::error("fail: send, queue_of(h) == nullptr\n"); - return false; - } - // calc a new message id - auto acc = info_of(h)->acc(); - if (acc == nullptr) { - ipc::error("fail: send, info_of(h)->acc() == nullptr\n"); - return false; - } - auto msg_id = acc->fetch_add(1, std::memory_order_relaxed); - auto try_push = std::forward(gen_push)(info_of(h), que, msg_id); - if (size > small_msg_limit) { - auto dat = info_of(h)->apply_storage(msg_id, size); - void * buf = shm::get_mem(dat, nullptr); - if (buf != nullptr) { - std::memcpy(buf, data, size); - info_of(h)->store(dat); - return try_push(static_cast(size) - static_cast(data_length), nullptr, 0); - } - // try using message fragment - ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size); - } - // push message fragment - int offset = 0; - for (int i = 0; i < static_cast(size / data_length); ++i, offset += data_length) { - if (!try_push(static_cast(size) - offset - static_cast(data_length), - static_cast(data) + offset, data_length)) { - return false; - } - info_of(h)->clear_store(); - } - // if remain > 0, this is the last message fragment - int remain = static_cast(size) - offset; - if (remain > 0) { - if (!try_push(remain - static_cast(data_length), - static_cast(data) + offset, static_cast(remain))) { - return false; - } - info_of(h)->clear_store(); - } - return true; -} - -static bool send(ipc::handle_t h, void const * data, std::size_t size) { - return send([](auto info, auto que, auto msg_id) { - return [info, que, msg_id](int remain, void const * data, std::size_t size) { - if (!wait_for(info->wt_waiter_, [&] { - return !que->push(info->cc_id_, msg_id, remain, data, size); - }, que->dis_flag() ? 0 : static_cast(default_timeut))) { - ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size); - if (!que->force_push(info->cc_id_, msg_id, remain, data, size)) { - return false; - } - } - info->rd_waiter_.broadcast(); - return true; - }; - }, h, data, size); -} - -static bool try_send(ipc::handle_t h, void const * data, std::size_t size) { - return send([](auto info, auto que, auto msg_id) { - return [info, que, msg_id](int remain, void const * data, std::size_t size) { - if (!wait_for(info->wt_waiter_, [&] { - return !que->push(info->cc_id_, msg_id, remain, data, size); - }, 0)) { - return false; - } - info->rd_waiter_.broadcast(); - return true; - }; - }, h, data, size); -} - -static buff_t recv(ipc::handle_t h, std::size_t tm) { - auto que = queue_of(h); - if (que == nullptr) { - ipc::error("fail: recv, queue_of(h) == nullptr\n"); - return {}; - } - if (que->connect()) { // wouldn't connect twice - info_of(h)->cc_waiter_.broadcast(); - } - auto& rc = info_of(h)->recv_cache(); - while (1) { - // pop a new message - typename queue_t::value_t msg; - if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] { return !que->pop(msg); }, tm)) { - return {}; - } - info_of(h)->wt_waiter_.broadcast(); - if ((info_of(h)->acc() != nullptr) && (msg.conn_ == info_of(h)->cc_id_)) { - continue; // ignore message to self - } - // msg.remain_ may minus & abs(msg.remain_) < data_length - auto remain = static_cast(static_cast(data_length) + msg.remain_); - // find cache with msg.id_ - auto cac_it = rc.find(msg.id_); - if (cac_it == rc.end()) { - if (remain <= data_length) { - return make_cache(msg.data_, remain); - } - if (msg.storage_) { - auto dat = info_of(h)->acquire_storage(msg.conn_, msg.id_); - std::size_t dat_sz = 0; - void * buf = shm::get_mem(dat, &dat_sz); - if (buf != nullptr && remain <= dat_sz) { - return buff_t { buf, remain, [](void * dat, std::size_t) { - shm::release(dat); - }, dat }; - } - else ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd, shm.size: %zd\n", - msg.id_, remain, dat_sz); - } - // gc - if (rc.size() > 1024) { - std::vector need_del; - for (auto const & pair : rc) { - auto cmp = std::minmax(msg.id_, pair.first); - if (cmp.second - cmp.first > 8192) { - need_del.push_back(pair.first); - } - } - for (auto id : need_del) rc.erase(id); - } - // cache the first message fragment - rc.emplace(msg.id_, cache_t { data_length, make_cache(msg.data_, remain) }); - } - // has cached before this message - else { - auto& cac = cac_it->second; - // this is the last message fragment - if (msg.remain_ <= 0) { - cac.append(&(msg.data_), remain); - // finish this message, erase it from cache - auto buff = std::move(cac.buff_); - rc.erase(cac_it); - return buff; - } - // there are remain datas after this message - cac.append(&(msg.data_), data_length); - } - } -} - -static buff_t try_recv(ipc::handle_t h) { - return recv(h, 0); -} - -}; // detail_impl - -template -using policy_t = policy::choose; - -} // internal-linkage - -namespace ipc { - -template -ipc::handle_t chan_impl::connect(char const * name, unsigned mode) { - return detail_impl>::connect(name, mode & receiver); -} - -template -void chan_impl::disconnect(ipc::handle_t h) { - detail_impl>::disconnect(h); -} - -template -char const * chan_impl::name(ipc::handle_t h) { - auto info = detail_impl>::info_of(h); - return (info == nullptr) ? nullptr : info->name_.c_str(); -} - -template -std::size_t chan_impl::recv_count(ipc::handle_t h) { - return detail_impl>::recv_count(h); -} - -template -bool chan_impl::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { - return detail_impl>::wait_for_recv(h, r_count, tm); -} - -template -bool chan_impl::send(ipc::handle_t h, void const * data, std::size_t size) { - return detail_impl>::send(h, data, size); -} - -template -buff_t chan_impl::recv(ipc::handle_t h, std::size_t tm) { - return detail_impl>::recv(h, tm); -} - -template -bool chan_impl::try_send(ipc::handle_t h, void const * data, std::size_t size) { - return detail_impl>::try_send(h, data, size); -} - -template -buff_t chan_impl::try_recv(ipc::handle_t h) { - return detail_impl>::try_recv(h); -} - -template struct chan_impl>; -template struct chan_impl>; -template struct chan_impl>; -template struct chan_impl>; -template struct chan_impl>; - -} // namespace ipc +#include "ipc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "shm.h" +#include "tls_pointer.h" +#include "pool_alloc.h" +#include "queue.h" +#include "policy.h" +#include "rw_lock.h" +#include "log.h" + +#include "memory/resource.h" + +#include "platform/detail.h" +#include "platform/waiter_wrapper.h" + +#include "circ/elem_array.h" + +namespace { + +using namespace ipc; +using msg_id_t = std::uint32_t; + +template +struct msg_t; + +template +struct msg_t<0, AlignSize> { + msg_id_t conn_; + msg_id_t id_; + std::int32_t remain_; + bool storage_; +}; + +template +struct msg_t : msg_t<0, AlignSize> { + std::aligned_storage_t data_ {}; + + msg_t() = default; + msg_t(msg_id_t c, msg_id_t i, std::int32_t r, void const * d, std::size_t s) + : msg_t<0, AlignSize> { c, i, r, (d == nullptr) || (s == 0) } { + if (!this->storage_) { + std::memcpy(&data_, d, s); + } + } +}; + +template +buff_t make_cache(T& data, std::size_t size) { + auto ptr = mem::alloc(size); + std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size)); + return { ptr, size, mem::free }; +} + +struct cache_t { + std::size_t fill_; + buff_t buff_; + + cache_t(std::size_t f, buff_t&& b) + : fill_(f), buff_(std::move(b)) + {} + + void append(void const * data, std::size_t size) { + if (fill_ >= buff_.size() || data == nullptr || size == 0) return; + auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size()); + std::memcpy(static_cast(buff_.data()) + fill_, data, new_fill - fill_); + fill_ = new_fill; + } +}; + +struct conn_info_head { + using acc_t = std::atomic; + + static auto cc_acc() { + static shm::handle acc_h("__CA_CONN__", sizeof(acc_t)); + return static_cast(acc_h.get()); + } + + ipc::string name_; + msg_id_t cc_id_; // connection-info id + waiter cc_waiter_, wt_waiter_, rd_waiter_; + shm::handle acc_h_; + + /* + * thread_local may have some bugs. + * + * + * - https://sourceforge.net/p/mingw-w64/bugs/727/ + * - https://sourceforge.net/p/mingw-w64/bugs/527/ + * - https://github.com/Alexpux/MINGW-packages/issues/2519 + * - https://github.com/ChaiScript/ChaiScript/issues/402 + * - https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html + * - https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827 + */ + tls::pointer> recv_cache_; + + struct simple_push { + + template + using elem_t = shm::id_t; + + circ::u2_t wt_; // write index + + constexpr circ::u2_t cursor() const noexcept { + return 0; + } + + template + bool push(W* /*wrapper*/, F&& f, E* elems) { + std::forward(f)(&(elems[circ::index_of(wt_)])); + ++ wt_; + return true; + } + }; + + circ::elem_array msg_datas_; + + conn_info_head(char const * name) + : name_ (name) + , cc_id_ ((cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed)) + , cc_waiter_(("__CC_CONN__" + name_).c_str()) + , wt_waiter_(("__WT_CONN__" + name_).c_str()) + , rd_waiter_(("__RD_CONN__" + name_).c_str()) + , acc_h_ (("__AC_CONN__" + name_).c_str(), sizeof(acc_t)) { + } + + auto acc() { + return static_cast(acc_h_.get()); + } + + auto& recv_cache() { + return *recv_cache_.create(); + } + + shm::id_t apply_storage(msg_id_t msg_id, std::size_t size) { + return shm::acquire( + ("__ST_CONN__" + ipc::to_string(cc_id_) + + "__" + ipc::to_string(msg_id)).c_str(), size, shm::create); + } + + static shm::id_t acquire_storage(msg_id_t cc_id, msg_id_t msg_id) { + return shm::acquire( + ("__ST_CONN__" + ipc::to_string(cc_id) + + "__" + ipc::to_string(msg_id)).c_str(), 0, shm::open); + } + + void store(shm::id_t dat) { + msg_datas_.push([dat](shm::id_t * id) { + (*id) = dat; + }); + } + + void clear_store() { + msg_datas_.push([](shm::id_t * id) { + if (*id == nullptr) return; + shm::remove(*id); + (*id) = nullptr; + }); + } +}; + +template +bool wait_for(W& waiter, F&& pred, std::size_t tm) { + if (tm == 0) return !pred(); + for (unsigned k = 0; pred();) { + bool loop = true, ret = true; + ipc::sleep(k, [&k, &loop, &ret, &waiter, &pred, tm] { + ret = waiter.wait_if([&loop, &pred] { + return loop = pred(); + }, tm); + k = 0; + return true; + }); + if (!ret ) return false; // timeout or fail + if (!loop) break; + } + return true; +} + +template +struct queue_generator { + + using queue_t = ipc::queue, Policy>; + + struct conn_info_t : conn_info_head { + queue_t que_; + + conn_info_t(char const * name) + : conn_info_head(name) + , que_(("__QU_CONN__" + + ipc::to_string(DataSize) + "__" + + ipc::to_string(AlignSize) + "__" + name).c_str()) { + } + }; +}; + +template +struct detail_impl { + +using queue_t = typename queue_generator::queue_t; +using conn_info_t = typename queue_generator::conn_info_t; + +constexpr static conn_info_t* info_of(ipc::handle_t h) { + return static_cast(h); +} + +constexpr static queue_t* queue_of(ipc::handle_t h) { + return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_); +} + +/* API implementations */ + +static ipc::handle_t connect(char const * name, bool start) { + auto h = mem::alloc(name); + auto que = queue_of(h); + if (que == nullptr) { + return nullptr; + } + if (start) { + if (que->connect()) { // wouldn't connect twice + info_of(h)->cc_waiter_.broadcast(); + } + } + return h; +} + +static void disconnect(ipc::handle_t h) { + auto que = queue_of(h); + if (que == nullptr) { + return; + } + if (que->disconnect()) { + info_of(h)->cc_waiter_.broadcast(); + } + mem::free(info_of(h)); +} + +static std::size_t recv_count(ipc::handle_t h) { + auto que = queue_of(h); + if (que == nullptr) { + return invalid_value; + } + return que->conn_count(); +} + +static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { + auto que = queue_of(h); + if (que == nullptr) { + return false; + } + return wait_for(info_of(h)->cc_waiter_, [que, r_count] { + return que->conn_count() < r_count; + }, tm); +} + +template +static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) { + if (data == nullptr || size == 0) { + ipc::error("fail: send(%p, %zd)\n", data, size); + return false; + } + auto que = queue_of(h); + if (que == nullptr) { + ipc::error("fail: send, queue_of(h) == nullptr\n"); + return false; + } + // calc a new message id + auto acc = info_of(h)->acc(); + if (acc == nullptr) { + ipc::error("fail: send, info_of(h)->acc() == nullptr\n"); + return false; + } + auto msg_id = acc->fetch_add(1, std::memory_order_relaxed); + auto try_push = std::forward(gen_push)(info_of(h), que, msg_id); + if (size > small_msg_limit) { + auto dat = info_of(h)->apply_storage(msg_id, size); + void * buf = shm::get_mem(dat, nullptr); + if (buf != nullptr) { + std::memcpy(buf, data, size); + info_of(h)->store(dat); + return try_push(static_cast(size) - static_cast(data_length), nullptr, 0); + } + // try using message fragment + ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size); + } + // push message fragment + int offset = 0; + for (int i = 0; i < static_cast(size / data_length); ++i, offset += data_length) { + if (!try_push(static_cast(size) - offset - static_cast(data_length), + static_cast(data) + offset, data_length)) { + return false; + } + info_of(h)->clear_store(); + } + // if remain > 0, this is the last message fragment + int remain = static_cast(size) - offset; + if (remain > 0) { + if (!try_push(remain - static_cast(data_length), + static_cast(data) + offset, static_cast(remain))) { + return false; + } + info_of(h)->clear_store(); + } + return true; +} + +static bool send(ipc::handle_t h, void const * data, std::size_t size) { + return send([](auto info, auto que, auto msg_id) { + return [info, que, msg_id](int remain, void const * data, std::size_t size) { + if (!wait_for(info->wt_waiter_, [&] { + return !que->push(info->cc_id_, msg_id, remain, data, size); + }, que->dis_flag() ? 0 : static_cast(default_timeut))) { + ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size); + if (!que->force_push(info->cc_id_, msg_id, remain, data, size)) { + return false; + } + } + info->rd_waiter_.broadcast(); + return true; + }; + }, h, data, size); +} + +static bool try_send(ipc::handle_t h, void const * data, std::size_t size) { + return send([](auto info, auto que, auto msg_id) { + return [info, que, msg_id](int remain, void const * data, std::size_t size) { + if (!wait_for(info->wt_waiter_, [&] { + return !que->push(info->cc_id_, msg_id, remain, data, size); + }, 0)) { + return false; + } + info->rd_waiter_.broadcast(); + return true; + }; + }, h, data, size); +} + +static buff_t recv(ipc::handle_t h, std::size_t tm) { + auto que = queue_of(h); + if (que == nullptr) { + ipc::error("fail: recv, queue_of(h) == nullptr\n"); + return {}; + } + if (que->connect()) { // wouldn't connect twice + info_of(h)->cc_waiter_.broadcast(); + } + auto& rc = info_of(h)->recv_cache(); + while (1) { + // pop a new message + typename queue_t::value_t msg; + if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] { return !que->pop(msg); }, tm)) { + return {}; + } + info_of(h)->wt_waiter_.broadcast(); + if ((info_of(h)->acc() != nullptr) && (msg.conn_ == info_of(h)->cc_id_)) { + continue; // ignore message to self + } + // msg.remain_ may minus & abs(msg.remain_) < data_length + auto remain = static_cast(static_cast(data_length) + msg.remain_); + // find cache with msg.id_ + auto cac_it = rc.find(msg.id_); + if (cac_it == rc.end()) { + if (remain <= data_length) { + return make_cache(msg.data_, remain); + } + if (msg.storage_) { + auto dat = info_of(h)->acquire_storage(msg.conn_, msg.id_); + std::size_t dat_sz = 0; + void * buf = shm::get_mem(dat, &dat_sz); + if (buf != nullptr && remain <= dat_sz) { + return buff_t { buf, remain, [](void * dat, std::size_t) { + shm::release(dat); + }, dat }; + } + else ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd, shm.size: %zd\n", + msg.id_, remain, dat_sz); + } + // gc + if (rc.size() > 1024) { + std::vector need_del; + for (auto const & pair : rc) { + auto cmp = std::minmax(msg.id_, pair.first); + if (cmp.second - cmp.first > 8192) { + need_del.push_back(pair.first); + } + } + for (auto id : need_del) rc.erase(id); + } + // cache the first message fragment + rc.emplace(msg.id_, cache_t { data_length, make_cache(msg.data_, remain) }); + } + // has cached before this message + else { + auto& cac = cac_it->second; + // this is the last message fragment + if (msg.remain_ <= 0) { + cac.append(&(msg.data_), remain); + // finish this message, erase it from cache + auto buff = std::move(cac.buff_); + rc.erase(cac_it); + return buff; + } + // there are remain datas after this message + cac.append(&(msg.data_), data_length); + } + } +} + +static buff_t try_recv(ipc::handle_t h) { + return recv(h, 0); +} + +}; // detail_impl + +template +using policy_t = policy::choose; + +} // internal-linkage + +namespace ipc { + +template +ipc::handle_t chan_impl::connect(char const * name, unsigned mode) { + return detail_impl>::connect(name, mode & receiver); +} + +template +void chan_impl::disconnect(ipc::handle_t h) { + detail_impl>::disconnect(h); +} + +template +char const * chan_impl::name(ipc::handle_t h) { + auto info = detail_impl>::info_of(h); + return (info == nullptr) ? nullptr : info->name_.c_str(); +} + +template +std::size_t chan_impl::recv_count(ipc::handle_t h) { + return detail_impl>::recv_count(h); +} + +template +bool chan_impl::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { + return detail_impl>::wait_for_recv(h, r_count, tm); +} + +template +bool chan_impl::send(ipc::handle_t h, void const * data, std::size_t size) { + return detail_impl>::send(h, data, size); +} + +template +buff_t chan_impl::recv(ipc::handle_t h, std::size_t tm) { + return detail_impl>::recv(h, tm); +} + +template +bool chan_impl::try_send(ipc::handle_t h, void const * data, std::size_t size) { + return detail_impl>::try_send(h, data, size); +} + +template +buff_t chan_impl::try_recv(ipc::handle_t h) { + return detail_impl>::try_recv(h); +} + +template struct chan_impl>; +template struct chan_impl>; +template struct chan_impl>; +template struct chan_impl>; +template struct chan_impl>; + +} // namespace ipc diff --git a/src/log.h b/src/log.h index a26d8bc..f09b385 100644 --- a/src/log.h +++ b/src/log.h @@ -1,39 +1,39 @@ -#pragma once - -#include -#include - -namespace ipc { -namespace detail { - -template -void print(O out, char const * fmt) { - std::fprintf(out, "%s", fmt); -} - -template -void print(O out, char const * fmt, P1&& p1, P&&... params) { - std::fprintf(out, fmt, std::forward(p1), std::forward

(params)...); -} - -} // namespace detail - -inline void log(char const * fmt) { - ipc::detail::print(stdout, fmt); -} - -template -void log(char const * fmt, P1&& p1, P&&... params) { - ipc::detail::print(stdout, fmt, std::forward(p1), std::forward

(params)...); -} - -inline void error(char const * fmt) { - ipc::detail::print(stderr, fmt); -} - -template -void error(char const * fmt, P1&& p1, P&&... params) { - ipc::detail::print(stderr, fmt, std::forward(p1), std::forward

(params)...); -} - -} // namespace ipc +#pragma once + +#include +#include + +namespace ipc { +namespace detail { + +template +void print(O out, char const * fmt) { + std::fprintf(out, "%s", fmt); +} + +template +void print(O out, char const * fmt, P1&& p1, P&&... params) { + std::fprintf(out, fmt, std::forward(p1), std::forward

(params)...); +} + +} // namespace detail + +inline void log(char const * fmt) { + ipc::detail::print(stdout, fmt); +} + +template +void log(char const * fmt, P1&& p1, P&&... params) { + ipc::detail::print(stdout, fmt, std::forward(p1), std::forward

(params)...); +} + +inline void error(char const * fmt) { + ipc::detail::print(stderr, fmt); +} + +template +void error(char const * fmt, P1&& p1, P&&... params) { + ipc::detail::print(stderr, fmt, std::forward(p1), std::forward

(params)...); +} + +} // namespace ipc diff --git a/src/memory/alloc.h b/src/memory/alloc.h index f5df02b..58f8bf8 100644 --- a/src/memory/alloc.h +++ b/src/memory/alloc.h @@ -1,431 +1,432 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "def.h" -#include "rw_lock.h" -#include "concept.h" - -#include "platform/detail.h" - -namespace ipc { -namespace mem { - -class static_alloc { -public: - static void swap(static_alloc&) {} - static void clear() {} - - static void* alloc(std::size_t size) { - return size ? std::malloc(size) : nullptr; - } - - static void free(void* p) { - std::free(p); - } - - static void free(void* p, std::size_t /*size*/) { - free(p); - } -}; - -//////////////////////////////////////////////////////////////// -/// Scope allocation -- The destructor will release all allocated blocks. -//////////////////////////////////////////////////////////////// - -namespace detail { - -constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { - return ((size - 1) & ~(alignment - 1)) + alignment; -} - -IPC_CONCEPT_(has_take, take(Type{})); - -class scope_alloc_base { -protected: - struct block_t { - block_t * next_; - std::size_t size_; - } * head_ = nullptr, * tail_ = nullptr; - - enum : std::size_t { - aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t)) - }; - -public: - void swap(scope_alloc_base & rhs) { - std::swap(head_, rhs.head_); - std::swap(tail_, rhs.tail_); - } - - bool empty() const noexcept { - return head_ == nullptr; - } - - void take(scope_alloc_base && rhs) { - if (rhs.empty()) return; - if (empty()) swap(rhs); - else { - std::swap(tail_->next_, rhs.head_); - // rhs.head_ should be nullptr here - tail_ = rhs.tail_; - rhs.tail_ = nullptr; - } - } - - void free(void* /*p*/) {} - void free(void* /*p*/, std::size_t) {} -}; - -} // namespace detail - -template -class scope_alloc : public detail::scope_alloc_base { -public: - using base_t = detail::scope_alloc_base; - using alloc_policy = AllocP; - -private: - alloc_policy alloc_; - - void free_all() { - while (!empty()) { - auto curr = head_; - head_ = head_->next_; - alloc_.free(curr, curr->size_); - } - // now head_ is nullptr - } - -public: - scope_alloc() = default; - - scope_alloc(scope_alloc&& rhs) { swap(rhs); } - scope_alloc& operator=(scope_alloc&& rhs) { swap(rhs); return (*this); } - - ~scope_alloc() { free_all(); } - - template - void set_allocator(A && alc) { - alloc_ = std::forward(alc); - } - - void swap(scope_alloc& rhs) { - alloc_.swap(rhs.alloc_); - base_t::swap(rhs); - } - - template - auto take(scope_alloc && rhs) -> ipc::require::value> { - base_t::take(std::move(rhs)); - alloc_.take(std::move(rhs.alloc_)); - } - - template - auto take(scope_alloc && rhs) -> ipc::require::value> { - base_t::take(std::move(rhs)); - } - - void clear() { - free_all(); - tail_ = nullptr; - alloc_.~alloc_policy(); - } - - void* alloc(std::size_t size) { - auto curr = static_cast(alloc_.alloc(size += aligned_block_size)); - curr->next_ = head_; - curr->size_ = size; - head_ = curr; - if (tail_ == nullptr) { - tail_ = curr; - } - return (reinterpret_cast(curr) + aligned_block_size); - } -}; - -//////////////////////////////////////////////////////////////// -/// Fixed-size blocks allocation -//////////////////////////////////////////////////////////////// - -namespace detail { - -class fixed_alloc_base { -protected: - std::size_t init_expand_; - void * cursor_; - - void init(std::size_t init_expand) { - init_expand_ = init_expand; - cursor_ = nullptr; - } - - static void** node_p(void* node) { - return reinterpret_cast(node); - } - - static auto& next(void* node) { - return *node_p(node); - } - -public: - void swap(fixed_alloc_base& rhs) { - std::swap(init_expand_, rhs.init_expand_); - std::swap(cursor_ , rhs.cursor_); - } - - bool empty() const noexcept { - return cursor_ == nullptr; - } - - void take(fixed_alloc_base && rhs) { - init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_); - if (rhs.empty()) return; - auto curr = cursor_; - if (curr != nullptr) while (1) { - auto next_cur = next(curr); - if (next_cur == nullptr) { - std::swap(next(curr), rhs.cursor_); - return; - } - // next_cur != nullptr - else curr = next_cur; - } - // curr == nullptr, means cursor_ == nullptr - else std::swap(cursor_, rhs.cursor_); - // rhs.cursor_ must be nullptr - } - - void free(void* p) { - if (p == nullptr) return; - next(p) = cursor_; - cursor_ = p; - } - - void free(void* p, std::size_t) { - free(p); - } -}; - -struct fixed_expand_policy { - - enum : std::size_t { - base_size = sizeof(void*) * 1024 / 2 - }; - - static std::size_t prev(std::size_t& e) { - if ((e /= 2) == 0) e = 1; - return e; - } - - static std::size_t next(std::size_t& e) { - return e *= 2; - } - - template - static std::size_t next(std::size_t & e) { - return ipc::detail::max(BlockSize, base_size) * next(e); - } -}; - -} // namespace detail - -template , - typename ExpandP = detail::fixed_expand_policy> -class fixed_alloc : public detail::fixed_alloc_base { -public: - using base_t = detail::fixed_alloc_base; - using alloc_policy = AllocP; - - enum : std::size_t { - block_size = (ipc::detail::max)(BlockSize, sizeof(void*)) - }; - -private: - alloc_policy alloc_; - - void* try_expand() { - if (empty()) { - auto size = ExpandP::template next(init_expand_); - auto p = node_p(cursor_ = alloc_.alloc(size)); - for (std::size_t i = 0; i < (size / block_size) - 1; ++i) - p = node_p((*p) = reinterpret_cast(p) + block_size); - (*p) = nullptr; - } - return cursor_; - } - -public: - explicit fixed_alloc(std::size_t init_expand = 1) { - init(init_expand); - } - - fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); } - fixed_alloc& operator=(fixed_alloc&& rhs) { swap(rhs); return (*this); } - - template - void set_allocator(A && alc) { - alloc_ = std::forward(alc); - } - - void swap(fixed_alloc& rhs) { - alloc_.swap(rhs.alloc_); - base_t::swap(rhs); - } - - template - auto take(fixed_alloc && rhs) -> ipc::require::value> { - base_t::take(std::move(rhs)); - alloc_.take(std::move(rhs.alloc_)); - } - - template - auto take(fixed_alloc && rhs) -> ipc::require::value> { - base_t::take(std::move(rhs)); - } - - void clear() { - ExpandP::prev(init_expand_); - cursor_ = nullptr; - alloc_.~alloc_policy(); - } - - void* alloc() { - void* p = try_expand(); - cursor_ = next(p); - return p; - } - - void* alloc(std::size_t) { - return alloc(); - } -}; - -//////////////////////////////////////////////////////////////// -/// Variable-size blocks allocation (without alignment) -//////////////////////////////////////////////////////////////// - -namespace detail { - -class variable_alloc_base { -protected: - struct head_t { - std::size_t free_; - } * head_ = nullptr; - - std::map> reserves_; - - enum : std::size_t { - aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) - }; - - static byte_t * buffer(head_t* p) { - return reinterpret_cast(p) + aligned_head_size + p->free_; - } - - std::size_t remain() const noexcept { - return (head_ == nullptr) ? 0 : head_->free_; - } - -public: - void swap(variable_alloc_base& rhs) { - std::swap(head_, rhs.head_); - } - - bool empty() const noexcept { - return remain() == 0; - } - - void take(variable_alloc_base && rhs) { - if (rhs.remain() > remain()) { - if (!empty()) { - reserves_.emplace(head_->free_, head_); - } - head_ = rhs.head_; - } - else if (!rhs.empty()) { - reserves_.emplace(rhs.head_->free_, rhs.head_); - } - rhs.head_ = nullptr; - } - - void free(void* /*p*/) {} - void free(void* /*p*/, std::size_t) {} -}; - -} // namespace detail - -template > -class variable_alloc : public detail::variable_alloc_base { -public: - using base_t = detail::variable_alloc_base; - using head_t = base_t::head_t; - using alloc_policy = AllocP; - -private: - alloc_policy alloc_; - -public: - variable_alloc() = default; - - variable_alloc(variable_alloc&& rhs) { swap(rhs); } - variable_alloc& operator=(variable_alloc&& rhs) { swap(rhs); return (*this); } - - template - void set_allocator(A && alc) { - alloc_ = std::forward(alc); - } - - void swap(variable_alloc& rhs) { - alloc_.swap(rhs.alloc_); - base_t::swap(rhs); - } - - template - auto take(variable_alloc && rhs) -> ipc::require::value> { - base_t::take(std::move(rhs)); - alloc_.take(std::move(rhs.alloc_)); - } - - template - auto take(variable_alloc && rhs) -> ipc::require::value> { - base_t::take(std::move(rhs)); - } - - void clear() { - alloc_.~alloc_policy(); - } - - void* alloc(std::size_t size) { - if (size >= (ChunkSize - aligned_head_size)) { - return alloc_.alloc(size); - } - if (remain() < size) { - auto it = reserves_.begin(); - if ((it == reserves_.end()) || (it->first < size)) { - head_ = static_cast(alloc_.alloc(ChunkSize)); - head_->free_ = ChunkSize - aligned_head_size - size; - } - else { - auto temp = it->second; - temp->free_ -= size; - reserves_.erase(it); - if (remain() < temp->free_) { - head_ = temp; - } - else return base_t::buffer(temp); - } - } - // size shouldn't be 0 here, otherwise behavior is undefined - else head_->free_ -= size; - return base_t::buffer(head_); - } -}; - -} // namespace mem -} // namespace ipc +#pragma once + +#include +#include +#include +#include +#include + +#include "def.h" +#include "rw_lock.h" +#include "concept.h" + +#include "platform/detail.h" + +namespace ipc { +namespace mem { + +class static_alloc { +public: + static void swap(static_alloc&) {} + static void clear() {} + + static void* alloc(std::size_t size) { + return size ? std::malloc(size) : nullptr; + } + + static void free(void* p) { + std::free(p); + } + + static void free(void* p, std::size_t /*size*/) { + free(p); + } +}; + +//////////////////////////////////////////////////////////////// +/// Scope allocation -- The destructor will release all allocated blocks. +//////////////////////////////////////////////////////////////// + +namespace detail { + +constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { + return ((size - 1) & ~(alignment - 1)) + alignment; +} + +IPC_CONCEPT_(has_take, take(Type{})); + +class scope_alloc_base { +protected: + struct block_t { + block_t * next_; + std::size_t size_; + } * head_ = nullptr, * tail_ = nullptr; + + enum : std::size_t { + aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t)) + }; + +public: + void swap(scope_alloc_base & rhs) { + std::swap(head_, rhs.head_); + std::swap(tail_, rhs.tail_); + } + + bool empty() const noexcept { + return head_ == nullptr; + } + + void take(scope_alloc_base && rhs) { + if (rhs.empty()) return; + if (empty()) swap(rhs); + else { + std::swap(tail_->next_, rhs.head_); + // rhs.head_ should be nullptr here + tail_ = rhs.tail_; + rhs.tail_ = nullptr; + } + } + + void free(void* /*p*/) {} + void free(void* /*p*/, std::size_t) {} +}; + +} // namespace detail + +template +class scope_alloc : public detail::scope_alloc_base { +public: + using base_t = detail::scope_alloc_base; + using alloc_policy = AllocP; + +private: + alloc_policy alloc_; + + void free_all() { + while (!empty()) { + auto curr = head_; + head_ = head_->next_; + alloc_.free(curr, curr->size_); + } + // now head_ is nullptr + } + +public: + scope_alloc() = default; + + scope_alloc(scope_alloc&& rhs) { swap(rhs); } + scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); } + + ~scope_alloc() { free_all(); } + + template + void set_allocator(A && alc) { + alloc_ = std::forward(alc); + } + + void swap(scope_alloc& rhs) { + alloc_.swap(rhs.alloc_); + base_t::swap(rhs); + } + + template + auto take(scope_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + alloc_.take(std::move(rhs.alloc_)); + } + + template + auto take(scope_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + } + + void clear() { + free_all(); + tail_ = nullptr; + alloc_.~alloc_policy(); + } + + void* alloc(std::size_t size) { + auto curr = static_cast(alloc_.alloc(size += aligned_block_size)); + curr->next_ = head_; + curr->size_ = size; + head_ = curr; + if (tail_ == nullptr) { + tail_ = curr; + } + return (reinterpret_cast(curr) + aligned_block_size); + } +}; + +//////////////////////////////////////////////////////////////// +/// Fixed-size blocks allocation +//////////////////////////////////////////////////////////////// + +namespace detail { + +class fixed_alloc_base { +protected: + std::size_t init_expand_; + void * cursor_; + + void init(std::size_t init_expand) { + init_expand_ = init_expand; + cursor_ = nullptr; + } + + static void** node_p(void* node) { + return reinterpret_cast(node); + } + + static auto& next(void* node) { + return *node_p(node); + } + +public: + void swap(fixed_alloc_base& rhs) { + std::swap(init_expand_, rhs.init_expand_); + std::swap(cursor_ , rhs.cursor_); + } + + bool empty() const noexcept { + return cursor_ == nullptr; + } + + void take(fixed_alloc_base && rhs) { + init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_); + if (rhs.empty()) return; + auto curr = cursor_; + if (curr != nullptr) while (1) { + auto next_cur = next(curr); + if (next_cur == nullptr) { + std::swap(next(curr), rhs.cursor_); + return; + } + // next_cur != nullptr + else curr = next_cur; + } + // curr == nullptr, means cursor_ == nullptr + else std::swap(cursor_, rhs.cursor_); + // rhs.cursor_ must be nullptr + } + + void free(void* p) { + if (p == nullptr) return; + next(p) = cursor_; + cursor_ = p; + } + + void free(void* p, std::size_t) { + free(p); + } +}; + +} // namespace detail + +struct fixed_expand_policy { + + enum : std::size_t { + base_size = sizeof(void*) * 1024 + }; + + constexpr static std::size_t prev(std::size_t e) noexcept { + return ((e / 2) == 0) ? 1 : (e / 2); + } + + constexpr static std::size_t next(std::size_t e) noexcept { + return e * 2; + } + + template + static std::size_t next(std::size_t & e) { + auto n = ipc::detail::max(BlockSize, base_size) * e; + e = next(e); + return n; + } +}; + +template , + typename ExpandP = fixed_expand_policy> +class fixed_alloc : public detail::fixed_alloc_base { +public: + using base_t = detail::fixed_alloc_base; + using alloc_policy = AllocP; + + enum : std::size_t { + block_size = (ipc::detail::max)(BlockSize, sizeof(void*)) + }; + +private: + alloc_policy alloc_; + + void* try_expand() { + if (empty()) { + auto size = ExpandP::template next(init_expand_); + auto p = node_p(cursor_ = alloc_.alloc(size)); + for (std::size_t i = 0; i < (size / block_size) - 1; ++i) + p = node_p((*p) = reinterpret_cast(p) + block_size); + (*p) = nullptr; + } + return cursor_; + } + +public: + explicit fixed_alloc(std::size_t init_expand = 1) { + init(init_expand); + } + + fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); } + fixed_alloc& operator=(fixed_alloc rhs) { swap(rhs); return (*this); } + + template + void set_allocator(A && alc) { + alloc_ = std::forward(alc); + } + + void swap(fixed_alloc& rhs) { + alloc_.swap(rhs.alloc_); + base_t::swap(rhs); + } + + template + auto take(fixed_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + alloc_.take(std::move(rhs.alloc_)); + } + + template + auto take(fixed_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + } + + void clear() { + init_expand_ = ExpandP::prev(init_expand_); + cursor_ = nullptr; + alloc_.~alloc_policy(); + } + + void* alloc() { + void* p = try_expand(); + cursor_ = next(p); + return p; + } + + void* alloc(std::size_t) { + return alloc(); + } +}; + +//////////////////////////////////////////////////////////////// +/// Variable-size blocks allocation (without alignment) +//////////////////////////////////////////////////////////////// + +namespace detail { + +class variable_alloc_base { +protected: + struct head_t { + std::size_t free_; + } * head_ = nullptr; + + std::map> reserves_; + + enum : std::size_t { + aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) + }; + + static byte_t * buffer(head_t* p) { + return reinterpret_cast(p) + aligned_head_size + p->free_; + } + +public: + void swap(variable_alloc_base& rhs) { + std::swap(head_, rhs.head_); + } + + std::size_t remain() const noexcept { + return (head_ == nullptr) ? 0 : head_->free_; + } + + bool empty() const noexcept { + return remain() == 0; + } + + void take(variable_alloc_base && rhs) { + if (rhs.remain() > remain()) { + if (!empty()) { + reserves_.emplace(head_->free_, head_); + } + head_ = rhs.head_; + } + else if (!rhs.empty()) { + reserves_.emplace(rhs.head_->free_, rhs.head_); + } + rhs.head_ = nullptr; + } + + void free(void* /*p*/) {} + void free(void* /*p*/, std::size_t) {} +}; + +} // namespace detail + +template > +class variable_alloc : public detail::variable_alloc_base { +public: + using base_t = detail::variable_alloc_base; + using head_t = base_t::head_t; + using alloc_policy = AllocP; + +private: + alloc_policy alloc_; + +public: + variable_alloc() = default; + + variable_alloc(variable_alloc&& rhs) { swap(rhs); } + variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); } + + template + void set_allocator(A && alc) { + alloc_ = std::forward(alc); + } + + void swap(variable_alloc& rhs) { + alloc_.swap(rhs.alloc_); + base_t::swap(rhs); + } + + template + auto take(variable_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + alloc_.take(std::move(rhs.alloc_)); + } + + template + auto take(variable_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + } + + void clear() { + alloc_.~alloc_policy(); + } + + void* alloc(std::size_t size) { + if (size >= ChunkSize) { + return alloc_.alloc(size); + } + if (remain() < size) { + auto it = reserves_.begin(); + if ((it == reserves_.end()) || (it->first < size)) { + head_ = static_cast(alloc_.alloc(ChunkSize + aligned_head_size)); + head_->free_ = ChunkSize - size; + } + else { + auto temp = it->second; + temp->free_ -= size; + reserves_.erase(it); + if (remain() < temp->free_) { + head_ = temp; + } + else return base_t::buffer(temp); + } + } + // size shouldn't be 0 here, otherwise behavior is undefined + else head_->free_ -= size; + return base_t::buffer(head_); + } +}; + +} // namespace mem +} // namespace ipc diff --git a/src/memory/resource.h b/src/memory/resource.h old mode 100755 new mode 100644 index d312cc7..c9e3994 --- a/src/memory/resource.h +++ b/src/memory/resource.h @@ -1,68 +1,74 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "def.h" - -#include "memory/alloc.h" -#include "memory/wrapper.h" -#include "platform/detail.h" - -namespace ipc { -namespace mem { - -using chunk_variable_alloc = variable_alloc; - -template -using static_async_fixed = static_wrapper>>; - -using async_pool_alloc = static_alloc/*variable_wrapper*/; - -template -using allocator = allocator_wrapper; - -} // namespace mem - -namespace { - -constexpr char const * pf(int) { return "%d" ; } -constexpr char const * pf(long) { return "%ld" ; } -constexpr char const * pf(long long) { return "%lld"; } -constexpr char const * pf(unsigned int) { return "%u" ; } -constexpr char const * pf(unsigned long) { return "%lu" ; } -constexpr char const * pf(unsigned long long) { return "%llu"; } -constexpr char const * pf(float) { return "%f" ; } -constexpr char const * pf(double) { return "%f" ; } -constexpr char const * pf(long double) { return "%Lf" ; } - -} // internal-linkage - -template -using unordered_map = std::unordered_map< - Key, T, std::hash, std::equal_to, ipc::mem::allocator> ->; - -template -using basic_string = std::basic_string< - Char, std::char_traits, ipc::mem::allocator ->; - -using string = basic_string; -using wstring = basic_string; - -template -ipc::string to_string(T val) { - char buf[std::numeric_limits::digits10 + 1] {}; - if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) { - return buf; - } - return {}; -} - -} // namespace ipc +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +#include "memory/alloc.h" +#include "memory/wrapper.h" +#include "platform/detail.h" + +namespace ipc { +namespace mem { + +namespace detail { + +using chunk_variable_alloc = + static_wrapper>>; + +template +using static_async_fixed = + static_wrapper>>; + +using async_pool_alloc = /*static_alloc*/variable_wrapper; + +template +using allocator = allocator_wrapper; + +} // namespace mem + +namespace { + +constexpr char const * pf(int) { return "%d" ; } +constexpr char const * pf(long) { return "%ld" ; } +constexpr char const * pf(long long) { return "%lld"; } +constexpr char const * pf(unsigned int) { return "%u" ; } +constexpr char const * pf(unsigned long) { return "%lu" ; } +constexpr char const * pf(unsigned long long) { return "%llu"; } +constexpr char const * pf(float) { return "%f" ; } +constexpr char const * pf(double) { return "%f" ; } +constexpr char const * pf(long double) { return "%Lf" ; } + +} // internal-linkage + +template +using unordered_map = std::unordered_map< + Key, T, std::hash, std::equal_to, ipc::mem::allocator> +>; + +template +using basic_string = std::basic_string< + Char, std::char_traits, ipc::mem::allocator +>; + +using string = basic_string; +using wstring = basic_string; + +template +ipc::string to_string(T val) { + char buf[std::numeric_limits::digits10 + 1] {}; + if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) { + return buf; + } + return {}; +} + +} // namespace ipc diff --git a/src/memory/wrapper.h b/src/memory/wrapper.h index bbc9f72..177180c 100644 --- a/src/memory/wrapper.h +++ b/src/memory/wrapper.h @@ -1,379 +1,403 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "def.h" -#include "rw_lock.h" -#include "tls_pointer.h" -#include "concept.h" - -#include "memory/alloc.h" -#include "platform/detail.h" - -namespace ipc { -namespace mem { - -//////////////////////////////////////////////////////////////// -/// The allocator wrapper class for STL -//////////////////////////////////////////////////////////////// - -template -class allocator_wrapper { - - template - friend class allocator_wrapper; - -public: - // type definitions - typedef T value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - typedef AllocP alloc_policy; - -private: - alloc_policy alloc_; - -public: - allocator_wrapper(void) noexcept = default; - - allocator_wrapper(const allocator_wrapper& rhs) noexcept - : alloc_(rhs.alloc_) - {} - - template - allocator_wrapper(const allocator_wrapper& rhs) noexcept - : alloc_(rhs.alloc_) - {} - - allocator_wrapper(allocator_wrapper&& rhs) noexcept - : alloc_(std::move(rhs.alloc_)) - {} - - template - allocator_wrapper(allocator_wrapper&& rhs) noexcept - : alloc_(std::move(rhs.alloc_)) - {} - - allocator_wrapper(const AllocP& rhs) noexcept - : alloc_(rhs) - {} - - allocator_wrapper(AllocP&& rhs) noexcept - : alloc_(std::move(rhs)) - {} - -public: - // the other type of std_allocator - template - struct rebind { typedef allocator_wrapper other; }; - - constexpr size_type max_size(void) const noexcept { - return (std::numeric_limits::max)() / sizeof(T); - } - -public: - pointer allocate(size_type count) noexcept { - if (count == 0) return nullptr; - if (count > this->max_size()) return nullptr; - return static_cast(alloc_.alloc(count * sizeof(T))); - } - - void deallocate(pointer p, size_type count) noexcept { - alloc_.free(p, count * sizeof(T)); - } - - template - static void construct(pointer p, P&&... params) { - ::new (static_cast(p)) T(std::forward

(params)...); - } - - static void destroy(pointer p) { - p->~T(); - } -}; - -template -class allocator_wrapper { -public: - // type definitions - typedef void value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - typedef AllocP alloc_policy; -}; - -template -constexpr bool operator==(const allocator_wrapper&, const allocator_wrapper&) noexcept { - return true; -} - -template -constexpr bool operator!=(const allocator_wrapper&, const allocator_wrapper&) noexcept { - return false; -} - -//////////////////////////////////////////////////////////////// -/// Thread-safe allocation wrapper -//////////////////////////////////////////////////////////////// - -template -class default_alloc_recoverer { -public: - using alloc_policy = AllocP; - -private: - ipc::spin_lock master_lock_; - std::vector master_allocs_; - -public: - void swap(default_alloc_recoverer& rhs) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); - master_allocs_.swap(rhs.master_allocs_); - } - - void clear() { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); - master_allocs_.clear(); - } - - void try_recover(alloc_policy & alc) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); - if (!master_allocs_.empty()) { - alc.swap(master_allocs_.back()); - master_allocs_.pop_back(); - } - } - - template - auto try_replenish(alloc_policy & alc) -> ipc::require::value> { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); - if (!master_allocs_.empty()) { - alc.take(std::move(master_allocs_.back())); - master_allocs_.pop_back(); - } - } - - template - constexpr auto try_replenish(alloc_policy & /*alc*/) noexcept - -> ipc::require::value> {} - - void collect(alloc_policy && alc) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); - master_allocs_.emplace_back(std::move(alc)); - } -}; - -template class RecovererP = default_alloc_recoverer> -class async_wrapper { -public: - using alloc_policy = AllocP; - -private: - RecovererP recoverer_; - - class alloc_proxy : public AllocP { - async_wrapper * w_ = nullptr; - - IPC_CONCEPT_(has_empty, empty()); - - public: - alloc_proxy(alloc_proxy && rhs) - : AllocP(std::move(rhs)) - {} - - alloc_proxy(async_wrapper* w) - : AllocP(), w_(w) { - if (w_ == nullptr) return; - w_->recoverer_.try_recover(*this); - } - - ~alloc_proxy() { - if (w_ == nullptr) return; - w_->recoverer_.collect(std::move(*this)); - } - - template - auto alloc(std::size_t size) -> ipc::require::value, void*> { - auto p = AllocP::alloc(size); - if (AllocP::empty() && (w_ != nullptr)) { - w_->recoverer_.try_replenish(*this); - } - return p; - } - - template - auto alloc(std::size_t size) -> ipc::require::value, void*> { - return AllocP::alloc(size); - } - }; - - friend class alloc_proxy; - - auto& get_alloc() { - static tls::pointer tls_alc; - return *tls_alc.create(this); - } - -public: - void swap(async_wrapper& rhs) { - recoverer_.swap(rhs.recoverer_); - } - - void clear() { - recoverer_.clear(); - } - - void* alloc(std::size_t size) { - return get_alloc().alloc(size); - } - - void free(void* p, std::size_t size) { - get_alloc().free(p, size); - } -}; - -//////////////////////////////////////////////////////////////// -/// Thread-safe allocation wrapper (with spin_lock) -//////////////////////////////////////////////////////////////// - -template -class sync_wrapper { -public: - using alloc_policy = AllocP; - using mutex_type = MutexT; - -private: - mutex_type lock_; - alloc_policy alloc_; - -public: - void swap(sync_wrapper& rhs) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); - alloc_.swap(rhs.alloc_); - } - - void clear() { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); - alloc_.~alloc_policy(); - } - - void* alloc(std::size_t size) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); - return alloc_.alloc(size); - } - - void free(void* p, std::size_t size) { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); - alloc_.free(p, size); - } -}; - -//////////////////////////////////////////////////////////////// -/// Static allocation wrapper -//////////////////////////////////////////////////////////////// - -template -class static_wrapper { -public: - using alloc_policy = AllocP; - - static alloc_policy& instance() { - static alloc_policy alloc; - return alloc; - } - - static void swap(static_wrapper&) {} - - static void clear() { - instance().clear(); - } - - static void* alloc(std::size_t size) { - return instance().alloc(size); - } - - static void free(void* p, std::size_t size) { - instance().free(p, size); - } -}; - -//////////////////////////////////////////////////////////////// -/// Variable memory allocation wrapper -//////////////////////////////////////////////////////////////// - -template -struct default_mapping_policy { - - enum : std::size_t { - base_size = BaseSize, - classes_size = 32 - }; - - static const std::size_t table[classes_size]; - - constexpr static std::size_t classify(std::size_t size) { - return (((size - 1) / base_size) < classes_size) ? - // always uses default_mapping_policy::table - default_mapping_policy<>::table[((size - 1) / base_size)] : classes_size; - } -}; - -template -const std::size_t default_mapping_policy::table[default_mapping_policy::classes_size] = { - /* 1 - 8 ~ 32 */ - 0 , 1 , 2 , 3 , - /* 2 - 48 ~ 256 */ - 5 , 5 , 7 , 7 , 9 , 9 , 11, 11, 13, 13, 15, 15, 17, 17, - 19, 19, 21, 21, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31 -}; - -template