mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 17:26:48 +08:00
add multi-wait (not ready, TBD)
This commit is contained in:
parent
e86d3e10e1
commit
c667f1513f
@ -1,5 +1,6 @@
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "ipc.h"
|
||||
#include "shm.h"
|
||||
@ -14,7 +15,7 @@ using namespace ipc;
|
||||
|
||||
struct ch_info_t {
|
||||
rw_lock lc_;
|
||||
id_pool ch_acc_; // only support 255 channels with one name
|
||||
id_pool<> ch_acc_; // only support 255 channels with one name
|
||||
};
|
||||
|
||||
struct ch_multi_routes {
|
||||
@ -24,7 +25,7 @@ struct ch_multi_routes {
|
||||
std::size_t id_;
|
||||
bool marked_ = false;
|
||||
|
||||
std::array<route, id_pool::max_count> rts_;
|
||||
std::array<route, decltype(ch_info_t::ch_acc_)::max_count> rts_;
|
||||
|
||||
ch_info_t& info() {
|
||||
return *static_cast<ch_info_t*>(h_.get());
|
||||
|
||||
@ -3,9 +3,41 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "def.h"
|
||||
#include "platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct id_type {
|
||||
uint_t<8> id_;
|
||||
alignas(AlignSize) byte_t data_[DataSize] {};
|
||||
|
||||
id_type& operator=(uint_t<8> val) {
|
||||
id_ = val;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
operator uint_t<8>() const {
|
||||
return id_;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t AlignSize>
|
||||
struct id_type<0, AlignSize> {
|
||||
uint_t<8> id_;
|
||||
|
||||
id_type& operator=(uint_t<8> val) {
|
||||
id_ = val;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
operator uint_t<8>() const {
|
||||
return id_;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t DataSize = 0,
|
||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::size_t))>
|
||||
class id_pool {
|
||||
public:
|
||||
enum : std::size_t {
|
||||
@ -13,9 +45,10 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
id_type<DataSize, AlignSize> next_[max_count];
|
||||
|
||||
uint_t<8> acquir_ = 0;
|
||||
uint_t<8> cursor_ = 0;
|
||||
uint_t<8> next_[max_count] {};
|
||||
|
||||
public:
|
||||
void init() {
|
||||
@ -49,12 +82,13 @@ public:
|
||||
}
|
||||
|
||||
bool release(std::size_t id) {
|
||||
if (id == invalid_value) return false;
|
||||
if (acquir_ == max_count) return false;
|
||||
if (acquir_ == id) {
|
||||
acquir_ = next_[id]; // point to next
|
||||
}
|
||||
else {
|
||||
auto a = next_[acquir_], l = acquir_;
|
||||
uint_t<8> a = next_[acquir_], l = acquir_;
|
||||
while (1) {
|
||||
if (a == max_count) {
|
||||
return false; // found nothing
|
||||
@ -76,10 +110,14 @@ public:
|
||||
void for_acquired(F&& fr) {
|
||||
auto a = acquir_;
|
||||
while (a != max_count) {
|
||||
fr(a);
|
||||
if (!fr(a)) return;
|
||||
a = next_[a];
|
||||
}
|
||||
}
|
||||
|
||||
void* at(std::size_t id) {
|
||||
return next_[id].data_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
27
src/ipc.cpp
27
src/ipc.cpp
@ -14,6 +14,7 @@
|
||||
#include "policy.h"
|
||||
|
||||
#include "memory/resource.h"
|
||||
#include "platform/detail.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -21,17 +22,8 @@ using namespace ipc;
|
||||
|
||||
using msg_id_t = std::size_t;
|
||||
|
||||
inline auto acc_of_msg() {
|
||||
static shm::handle g_shm { "GLOBAL_ACC_STORAGE__", sizeof(std::atomic<msg_id_t>) };
|
||||
return static_cast<std::atomic<msg_id_t>*>(g_shm.get());
|
||||
}
|
||||
|
||||
template <std::size_t DataSize,
|
||||
# if __cplusplus >= 201703L
|
||||
std::size_t AlignSize = (std::min)(DataSize, alignof(std::size_t))>
|
||||
# else /*__cplusplus < 201703L*/
|
||||
std::size_t AlignSize = (DataSize < alignof(std::size_t)) ? DataSize : alignof(std::size_t)>
|
||||
# endif/*__cplusplus < 201703L*/
|
||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::size_t))>
|
||||
struct msg_t;
|
||||
|
||||
template <std::size_t AlignSize>
|
||||
@ -141,8 +133,8 @@ static bool send(ipc::handle_t h, void const * data, std::size_t size) {
|
||||
if (que == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// calc a new message id, start with 1
|
||||
auto msg_id = acc_of_msg()->fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
// calc a new message id
|
||||
auto msg_id = ipc::detail::calc_unique_id();
|
||||
// push message fragment
|
||||
int offset = 0;
|
||||
for (int i = 0; i < static_cast<int>(size / data_length); ++i, offset += data_length) {
|
||||
@ -173,7 +165,7 @@ static buff_t recv(ipc::handle_t h) {
|
||||
while (1) {
|
||||
// pop a new message
|
||||
auto msg = que->pop();
|
||||
if (msg.head_.id_ == 0) return {};
|
||||
if (msg.head_.que_ == nullptr) return {};
|
||||
if (msg.head_.que_ == que) continue; // pop next
|
||||
// msg.head_.remain_ may minus & abs(msg.head_.remain_) < data_length
|
||||
std::size_t remain = static_cast<std::size_t>(
|
||||
@ -213,6 +205,15 @@ using policy_t = policy::choose<circ::elem_array, Flag>;
|
||||
|
||||
namespace ipc {
|
||||
|
||||
namespace detail {
|
||||
|
||||
std::size_t calc_unique_id() {
|
||||
static shm::handle g_shm { "__GLOBAL_ACC_STORAGE__", sizeof(std::atomic<std::size_t>) };
|
||||
return static_cast<std::atomic<std::size_t>*>(g_shm.get())->fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Flag>
|
||||
ipc::handle_t chan_impl<Flag>::connect(char const * name) {
|
||||
return detail_impl<policy_t<Flag>>::connect(name);
|
||||
|
||||
32
src/log.h
32
src/log.h
@ -4,10 +4,36 @@
|
||||
#include <utility>
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
template <typename... P>
|
||||
void log(char const * fmt, P&&... params) {
|
||||
std::fprintf(stderr, fmt, std::forward<P>(params)...);
|
||||
template <typename O>
|
||||
void print(O out, char const * fmt) {
|
||||
std::fprintf(out, "%s", fmt);
|
||||
}
|
||||
|
||||
template <typename O, typename P1, typename... P>
|
||||
void print(O out, char const * fmt, P1&& p1, P&&... params) {
|
||||
std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline void log(char const * fmt) {
|
||||
ipc::detail::print(stdout, fmt);
|
||||
}
|
||||
|
||||
template <typename P1, typename... P>
|
||||
void log(char const * fmt, P1&& p1, P&&... params) {
|
||||
ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
inline void error(char const * fmt) {
|
||||
ipc::detail::print(stderr, fmt);
|
||||
}
|
||||
|
||||
template <typename P1, typename... P>
|
||||
void error(char const * fmt, P1&& p1, P&&... params) {
|
||||
ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
@ -150,11 +150,7 @@ public:
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
enum : std::size_t {
|
||||
# if __cplusplus >= 201703L
|
||||
block_size = (std::max)(BlockSize, sizeof(void*))
|
||||
# else /*__cplusplus < 201703L*/
|
||||
block_size = (BlockSize < sizeof(void*)) ? sizeof(void*) : BlockSize
|
||||
# endif/*__cplusplus < 201703L*/
|
||||
block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
#include <shared_mutex>
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include "def.h"
|
||||
|
||||
@ -55,6 +57,8 @@ namespace detail {
|
||||
using std::unique_ptr;
|
||||
using std::unique_lock;
|
||||
using std::shared_lock;
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
#else /*__cplusplus < 201703L*/
|
||||
|
||||
@ -79,8 +83,20 @@ constexpr auto shared_lock(T&& lc) {
|
||||
return std::shared_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T const & max(const T& a, const T& b) {
|
||||
return a < b ? b : a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T const & min(const T& a, const T& b) {
|
||||
return b < a ? b : a;
|
||||
}
|
||||
|
||||
#endif/*__cplusplus < 201703L*/
|
||||
|
||||
std::size_t calc_unique_id();
|
||||
|
||||
template <typename F, typename D>
|
||||
constexpr decltype(auto) static_switch(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) {
|
||||
return def();
|
||||
|
||||
@ -51,12 +51,12 @@ void* acquire(char const * name, std::size_t size) {
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH);
|
||||
if (fd == -1) {
|
||||
ipc::log("fail shm_open[%d]: %s\n", errno, name);
|
||||
ipc::error("fail shm_open[%d]: %s\n", errno, name);
|
||||
return nullptr;
|
||||
}
|
||||
size += sizeof(acc_t);
|
||||
if (::ftruncate(fd, static_cast<off_t>(size)) != 0) {
|
||||
ipc::log("fail ftruncate[%d]: %s\n", errno, name);
|
||||
ipc::error("fail ftruncate[%d]: %s\n", errno, name);
|
||||
::close(fd);
|
||||
::shm_unlink(op_name.c_str());
|
||||
return nullptr;
|
||||
@ -64,7 +64,7 @@ void* acquire(char const * name, std::size_t size) {
|
||||
void* mem = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
::close(fd);
|
||||
if (mem == MAP_FAILED) {
|
||||
ipc::log("fail mmap[%d]: %s\n", errno, name);
|
||||
ipc::error("fail mmap[%d]: %s\n", errno, name);
|
||||
::shm_unlink(op_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -35,12 +35,12 @@ void* acquire(char const * name, std::size_t size) {
|
||||
0, static_cast<DWORD>(size),
|
||||
ipc::detail::to_tchar(std::string{"__IPC_SHM__"} + name).c_str());
|
||||
if (h == NULL) {
|
||||
ipc::log("fail CreateFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||
ipc::error("fail CreateFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||
return nullptr;
|
||||
}
|
||||
LPVOID mem = ::MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
if (mem == NULL) {
|
||||
ipc::log("fail MapViewOfFile[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||
ipc::error("fail MapViewOfFile[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||
::CloseHandle(h);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -4,133 +4,292 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
#include "def.h"
|
||||
#include "log.h"
|
||||
#include "shm.h"
|
||||
#include "rw_lock.h"
|
||||
#include "id_pool.h"
|
||||
#include "pool_alloc.h"
|
||||
|
||||
#include "platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
class condition {
|
||||
class mutex {
|
||||
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
public:
|
||||
bool open(char const * name) {
|
||||
if (name == nullptr || name[0] == '\0') return false;
|
||||
pthread_mutex_t& native() {
|
||||
return mutex_;
|
||||
}
|
||||
|
||||
bool open() {
|
||||
int eno;
|
||||
// init mutex
|
||||
pthread_mutexattr_t mutex_attr;
|
||||
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
|
||||
ipc::log("fail pthread_mutexattr_init[%d]: %s\n", eno, name);
|
||||
ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy);
|
||||
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||
ipc::log("fail pthread_mutexattr_setpshared[%d]: %s\n", eno, name);
|
||||
ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
if ((eno = ::pthread_mutex_init(&mutex_, &mutex_attr)) != 0) {
|
||||
ipc::log("fail pthread_mutex_init[%d]: %s\n", eno, name);
|
||||
ipc::error("fail pthread_mutex_init[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
auto guard_mutex = unique_ptr(&mutex_, ::pthread_mutex_destroy);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool close() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_mutex_destroy(&mutex_)) != 0) {
|
||||
ipc::error("fail pthread_mutex_destroy[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lock() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_mutex_lock(&mutex_)) != 0) {
|
||||
ipc::error("fail pthread_mutex_lock[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unlock() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_mutex_unlock(&mutex_)) != 0) {
|
||||
ipc::error("fail pthread_mutex_unlock[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class condition {
|
||||
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
public:
|
||||
bool open() {
|
||||
int eno;
|
||||
// init condition
|
||||
pthread_condattr_t cond_attr;
|
||||
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
|
||||
ipc::log("fail pthread_condattr_init[%d]: %s\n", eno, name);
|
||||
ipc::error("fail pthread_condattr_init[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy);
|
||||
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||
ipc::log("fail pthread_condattr_setpshared[%d]: %s\n", eno, name);
|
||||
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
if ((eno = ::pthread_cond_init(&cond_, &cond_attr)) != 0) {
|
||||
ipc::log("fail pthread_cond_init[%d]: %s\n", eno, name);
|
||||
ipc::error("fail pthread_cond_init[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
// no need to guard condition
|
||||
// release guards
|
||||
guard_mutex.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool close() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_cond_destroy(&cond_)) != 0) {
|
||||
ipc::error("fail pthread_cond_destroy[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wait(mutex& mtx) {
|
||||
int eno;
|
||||
if ((eno = ::pthread_cond_wait(&cond_, &mtx.native())) != 0) {
|
||||
ipc::error("fail pthread_cond_wait[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool notify() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_cond_signal(&cond_)) != 0) {
|
||||
ipc::error("fail pthread_cond_signal[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool broadcast() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_cond_broadcast(&cond_)) != 0) {
|
||||
ipc::error("fail pthread_cond_broadcast[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class event {
|
||||
using cnt_t = std::atomic<std::size_t>;
|
||||
|
||||
struct info_t {
|
||||
cnt_t cnt_;
|
||||
mutex mutex_;
|
||||
condition cond_;
|
||||
} * info_ = nullptr;
|
||||
|
||||
uint_t<16> wait_id_;
|
||||
|
||||
std::string name() const {
|
||||
return "__IPC_WAIT__" + std::to_string(wait_id_);
|
||||
}
|
||||
|
||||
void open() {
|
||||
auto n = name();
|
||||
info_ = static_cast<info_t*>(shm::acquire(n.c_str(), sizeof(info_t)));
|
||||
if (info_ == nullptr) {
|
||||
ipc::error("fail shm::acquire: %s\n", n.c_str());
|
||||
return;
|
||||
}
|
||||
if (info_->cnt_.fetch_add(1, std::memory_order_acq_rel) == 0) {
|
||||
if (!info_->mutex_.open()) return;
|
||||
if (!info_->cond_.open()) return;
|
||||
}
|
||||
info_->mutex_.lock();
|
||||
}
|
||||
|
||||
void close() {
|
||||
::pthread_cond_destroy(&cond_);
|
||||
::pthread_mutex_destroy(&mutex_);
|
||||
info_->mutex_.unlock();
|
||||
if (info_->cnt_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
info_->cond_.close();
|
||||
info_->mutex_.close();
|
||||
}
|
||||
shm::release(info_, sizeof(info_t));
|
||||
}
|
||||
|
||||
public:
|
||||
event(std::size_t id)
|
||||
: wait_id_(static_cast<uint_t<16>>(id)) {
|
||||
open();
|
||||
}
|
||||
|
||||
~event() {
|
||||
close();
|
||||
}
|
||||
|
||||
auto get_id() const noexcept {
|
||||
return wait_id_;
|
||||
}
|
||||
|
||||
bool wait() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_mutex_lock(&mutex_)) != 0) {
|
||||
ipc::log("fail pthread_mutex_lock[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
IPC_UNUSED_ auto guard = unique_ptr(&mutex_, ::pthread_mutex_unlock);
|
||||
if ((eno = ::pthread_cond_wait(&cond_, &mutex_)) != 0) {
|
||||
ipc::log("fail pthread_cond_wait[%d]\n", eno);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (info_ == nullptr) return false;
|
||||
return info_->cond_.wait(info_->mutex_);
|
||||
}
|
||||
|
||||
void notify() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_cond_signal(&cond_)) != 0) {
|
||||
ipc::log("fail pthread_cond_signal[%d]\n", eno);
|
||||
}
|
||||
}
|
||||
|
||||
void broadcast() {
|
||||
int eno;
|
||||
if ((eno = ::pthread_cond_broadcast(&cond_)) != 0) {
|
||||
ipc::log("fail pthread_cond_broadcast[%d]\n", eno);
|
||||
}
|
||||
bool notify() {
|
||||
if (info_ == nullptr) return false;
|
||||
return info_->cond_.notify();
|
||||
}
|
||||
};
|
||||
|
||||
class waiter {
|
||||
ipc::detail::condition cond_;
|
||||
using evt_id_t = decltype(std::declval<event>().get_id());
|
||||
|
||||
std::atomic<unsigned> counter_ { 0 };
|
||||
spin_lock evt_lc_;
|
||||
id_pool<sizeof(evt_id_t)> evt_ids_;
|
||||
|
||||
std::size_t push_event(event const & evt) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(evt_lc_);
|
||||
std::size_t id = evt_ids_.acquire();
|
||||
if (id == invalid_value) {
|
||||
ipc::error("fail push_event[has too many waiters]\n");
|
||||
return id;
|
||||
}
|
||||
(*static_cast<evt_id_t*>(evt_ids_.at(id))) = evt.get_id();
|
||||
evt_ids_.mark_acquired(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
void pop_event(std::size_t id) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(evt_lc_);
|
||||
evt_ids_.release(id);
|
||||
}
|
||||
|
||||
public:
|
||||
using handle_t = waiter*;
|
||||
|
||||
public:
|
||||
constexpr static handle_t invalid() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
handle_t open(char const * name) {
|
||||
if (name == nullptr || name[0] == '\0') return invalid();
|
||||
if ((counter_.fetch_add(1, std::memory_order_acq_rel) == 0) && !cond_.open(name)) {
|
||||
if (name == nullptr || name[0] == '\0') {
|
||||
return invalid();
|
||||
}
|
||||
if (counter_.fetch_add(1, std::memory_order_acq_rel) == 0) {
|
||||
evt_ids_.init();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void close(handle_t h) {
|
||||
if (h == invalid()) return;
|
||||
if (counter_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
cond_.close();
|
||||
counter_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
static bool wait_all(std::tuple<waiter*, handle_t> const * all, std::size_t size) {
|
||||
if (all == nullptr || size == 0) {
|
||||
return false;
|
||||
}
|
||||
// calc a new wait-id & construct event object
|
||||
event evt { ipc::detail::calc_unique_id() };
|
||||
auto ids = static_cast<std::size_t*>(mem::alloc(sizeof(std::size_t[size])));
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
ids[i] = std::get<0>(all[i])->push_event(evt);
|
||||
}
|
||||
IPC_UNUSED_ auto guard = unique_ptr(ids, [all, size](std::size_t* ids) {
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
std::get<0>(all[i])->pop_event(ids[i]);
|
||||
}
|
||||
mem::free(ids, sizeof(std::size_t[size]));
|
||||
});
|
||||
// wait for event signal
|
||||
return evt.wait();
|
||||
}
|
||||
|
||||
bool wait(handle_t h) {
|
||||
if (h == invalid()) return false;
|
||||
return cond_.wait();
|
||||
auto info = std::make_tuple(this, h);
|
||||
return wait_all(&info, 1);
|
||||
}
|
||||
|
||||
void notify(handle_t h) {
|
||||
if (h == invalid()) return;
|
||||
cond_.notify();
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(evt_lc_);
|
||||
evt_ids_.for_acquired([this](auto id) {
|
||||
event evt { *static_cast<evt_id_t*>(evt_ids_.at(id)) };
|
||||
evt.notify();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void broadcast(handle_t h) {
|
||||
if (h == invalid()) return;
|
||||
cond_.broadcast();
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(evt_lc_);
|
||||
evt_ids_.for_acquired([this](auto id) {
|
||||
event evt { *static_cast<evt_id_t*>(evt_ids_.at(id)) };
|
||||
evt.notify();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -5,9 +5,14 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
#include "rw_lock.h"
|
||||
#include "pool_alloc.h"
|
||||
|
||||
#include "platform/to_tchar.h"
|
||||
#include "platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
@ -32,6 +37,24 @@ public:
|
||||
::CloseHandle(h);
|
||||
}
|
||||
|
||||
static bool wait_all(std::tuple<waiter*, handle_t> const * all, std::size_t size) {
|
||||
if (all == nullptr || size == 0) {
|
||||
return false;
|
||||
}
|
||||
auto hs = static_cast<handle_t*>(mem::alloc(sizeof(handle_t[size])));
|
||||
IPC_UNUSED_ auto guard = unique_ptr(hs, [size](void* p) { mem::free(p, sizeof(handle_t[size])); });
|
||||
std::size_t i = 0;
|
||||
for (; i < size; ++i) {
|
||||
auto& info = all[i];
|
||||
if ((std::get<0>(all[i]) == nullptr) ||
|
||||
(std::get<1>(all[i]) == invalid())) continue;
|
||||
std::get<0>(info)->counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
hs[i] = std::get<1>(all[i]);
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
return ::WaitForMultipleObjects(hs, i, FALSE, INFINITE) != WAIT_FAILED;
|
||||
}
|
||||
|
||||
bool wait(handle_t h) {
|
||||
if (h == invalid()) return false;
|
||||
counter_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "pool_alloc.h"
|
||||
|
||||
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
||||
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
||||
@ -9,6 +13,7 @@
|
||||
#else
|
||||
#include "platform/waiter_linux.h"
|
||||
#endif
|
||||
#include "platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
@ -21,6 +26,10 @@ private:
|
||||
waiter_t* w_ = nullptr;
|
||||
waiter_t::handle_t h_ = waiter_t::invalid();
|
||||
|
||||
auto to_w_info() {
|
||||
return std::make_tuple(w_, h_);
|
||||
}
|
||||
|
||||
public:
|
||||
waiter_wrapper() = default;
|
||||
explicit waiter_wrapper(waiter_t* w) {
|
||||
@ -53,6 +62,22 @@ public:
|
||||
h_ = waiter_t::invalid();
|
||||
}
|
||||
|
||||
bool wait_all(waiter_wrapper * all, std::size_t size) {
|
||||
if (all == nullptr || size == 0) {
|
||||
return false;
|
||||
}
|
||||
using tp_t = decltype(std::declval<waiter_wrapper>().to_w_info());
|
||||
auto hs = static_cast<tp_t*>(mem::alloc(sizeof(tp_t[size])));
|
||||
IPC_UNUSED_ auto guard = unique_ptr(hs, [size](void* p) { mem::free(p, sizeof(tp_t[size])); });
|
||||
std::size_t i = 0;
|
||||
for (; i < size; ++i) {
|
||||
auto& w = all[i];
|
||||
if (!w.valid()) continue;
|
||||
hs[i] = w.to_w_info();
|
||||
}
|
||||
return waiter_t::wait_all(hs, i);
|
||||
}
|
||||
|
||||
bool wait() {
|
||||
if (!valid()) return false;
|
||||
return w_->wait(h_);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user