mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
fix bugs for waiter of linux (still has some bugs in win, multi-wait is TBD)
This commit is contained in:
parent
eef3cc01f0
commit
e94318c9a6
@ -23,123 +23,161 @@
|
|||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class semaphore {
|
class mutex {
|
||||||
|
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using handle_t = sem_t*;
|
pthread_mutex_t& native() {
|
||||||
|
return mutex_;
|
||||||
constexpr static handle_t invalid() {
|
|
||||||
return SEM_FAILED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle_t open(char const* name) {
|
bool open() {
|
||||||
handle_t sem = ::sem_open(name, O_CREAT, 0666, 0);
|
int eno;
|
||||||
if (sem == SEM_FAILED) {
|
// init mutex
|
||||||
ipc::error("fail sem_open[%d]: %s\n", errno, name);
|
pthread_mutexattr_t mutex_attr;
|
||||||
return invalid();
|
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
|
||||||
|
ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return sem;
|
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::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((eno = ::pthread_mutex_init(&mutex_, &mutex_attr)) != 0) {
|
||||||
|
ipc::error("fail pthread_mutex_init[%d]\n", eno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma push_macro("IPC_SEMAPHORE_FUNC_")
|
bool close() {
|
||||||
#undef IPC_SEMAPHORE_FUNC_
|
int eno;
|
||||||
#define IPC_SEMAPHORE_FUNC_(CALL, PAR) \
|
if ((eno = ::pthread_mutex_destroy(&mutex_)) != 0) {
|
||||||
if (::CALL(PAR) != 0) { \
|
ipc::error("fail pthread_mutex_destroy[%d]\n", eno);
|
||||||
ipc::error("fail " #CALL "[%d]\n", errno); \
|
return false;
|
||||||
return false; \
|
}
|
||||||
} \
|
return true;
|
||||||
return true
|
|
||||||
|
|
||||||
static bool close(handle_t h) {
|
|
||||||
if (h == invalid()) return false;
|
|
||||||
IPC_SEMAPHORE_FUNC_(sem_close, h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool destroy(char const* name) {
|
bool lock() {
|
||||||
IPC_SEMAPHORE_FUNC_(sem_unlink, name);
|
int eno;
|
||||||
|
if ((eno = ::pthread_mutex_lock(&mutex_)) != 0) {
|
||||||
|
ipc::error("fail pthread_mutex_lock[%d]\n", eno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool post(handle_t h) {
|
bool unlock() {
|
||||||
if (h == invalid()) return false;
|
int eno;
|
||||||
IPC_SEMAPHORE_FUNC_(sem_post, h);
|
if ((eno = ::pthread_mutex_unlock(&mutex_)) != 0) {
|
||||||
|
ipc::error("fail pthread_mutex_unlock[%d]\n", eno);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
static bool wait(handle_t h) {
|
|
||||||
if (h == invalid()) return false;
|
|
||||||
IPC_SEMAPHORE_FUNC_(sem_wait, h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma pop_macro("IPC_SEMAPHORE_FUNC_")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class event {
|
class condition {
|
||||||
std::atomic<std::size_t>* cnt_ = nullptr;
|
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
|
||||||
semaphore::handle_t sem_ = semaphore::invalid();
|
|
||||||
std::size_t wait_id_;
|
|
||||||
|
|
||||||
std::string name() const {
|
|
||||||
return "__IPC_WAIT__" + std::to_string(wait_id_);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
event(std::size_t id)
|
bool open() {
|
||||||
: wait_id_(static_cast<uint_t<16>>(id)) {
|
int eno;
|
||||||
auto n = name();
|
// init condition
|
||||||
cnt_ = static_cast<std::atomic<std::size_t>*>(
|
pthread_condattr_t cond_attr;
|
||||||
shm::acquire(n.c_str(), sizeof(std::atomic<std::size_t>)));
|
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
|
||||||
if (cnt_ == nullptr) {
|
ipc::error("fail pthread_condattr_init[%d]\n", eno);
|
||||||
ipc::error("fail shm::acquire: %s\n", n.c_str());
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
cnt_->fetch_add(1, std::memory_order_acquire);
|
IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy);
|
||||||
sem_ = semaphore::open(n.c_str());
|
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||||
|
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((eno = ::pthread_cond_init(&cond_, &cond_attr)) != 0) {
|
||||||
|
ipc::error("fail pthread_cond_init[%d]\n", eno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
~event() {
|
bool close() {
|
||||||
semaphore::close(sem_);
|
int eno;
|
||||||
if (cnt_->fetch_sub(1, std::memory_order_release) == 1) {
|
if ((eno = ::pthread_cond_destroy(&cond_)) != 0) {
|
||||||
semaphore::destroy(name().c_str());
|
ipc::error("fail pthread_cond_destroy[%d]\n", eno);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
shm::release(cnt_, sizeof(std::atomic<std::size_t>));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto get_id() const noexcept {
|
bool wait(mutex& mtx) {
|
||||||
return wait_id_;
|
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 wait() {
|
|
||||||
if (sem_ == semaphore::invalid()) return false;
|
|
||||||
return semaphore::wait(sem_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify() {
|
bool notify() {
|
||||||
if (sem_ == semaphore::invalid()) return false;
|
int eno;
|
||||||
return semaphore::post(sem_);
|
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 semaphore {
|
||||||
|
mutex lock_;
|
||||||
|
condition cond_;
|
||||||
|
long counter_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool open() {
|
||||||
|
return lock_.open() && cond_.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
cond_.close();
|
||||||
|
lock_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void wait_if(F&& check) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
|
while ((counter_ <= 0) && std::forward<F>(check)()) {
|
||||||
|
cond_.wait(lock_);
|
||||||
|
}
|
||||||
|
-- counter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
bool post(F&& count) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
|
auto c = std::forward<F>(count)();
|
||||||
|
if (c <= 0) return false;
|
||||||
|
counter_ += c;
|
||||||
|
return cond_.broadcast();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class waiter {
|
class waiter {
|
||||||
using evt_id_t = decltype(std::declval<event>().get_id());
|
semaphore sem_;
|
||||||
|
std::atomic<long> counter_ { 0 };
|
||||||
std::atomic<unsigned> counter_ { 0 };
|
std::atomic<unsigned> opened_ { 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:
|
public:
|
||||||
using handle_t = waiter * ;
|
using handle_t = waiter * ;
|
||||||
@ -152,60 +190,48 @@ public:
|
|||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
if (counter_.fetch_add(1, std::memory_order_acq_rel) == 0) {
|
if ((opened_.fetch_add(1, std::memory_order_acq_rel) == 0) && !sem_.open()) {
|
||||||
evt_ids_.init();
|
return invalid();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close(handle_t h) {
|
void close(handle_t h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
counter_.fetch_sub(1, std::memory_order_acq_rel);
|
if (opened_.fetch_sub(1, std::memory_order_release) == 1) {
|
||||||
|
sem_.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool wait_all(std::tuple<waiter*, handle_t> const * all, std::size_t size) {
|
template <typename F>
|
||||||
|
static bool multi_wait_if(std::tuple<waiter*, handle_t> const * all, std::size_t size, F&& /*check*/) {
|
||||||
if (all == nullptr || size == 0) {
|
if (all == nullptr || size == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// calc a new wait-id & construct event object
|
return true;
|
||||||
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) {
|
template <typename F>
|
||||||
|
bool wait_if(handle_t h, F&& check) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
auto info = std::make_tuple(this, h);
|
counter_.fetch_add(1, std::memory_order_release);
|
||||||
return wait_all(&info, 1);
|
IPC_UNUSED_ auto guard = unique_ptr(&counter_, [](decltype(counter_)* c) {
|
||||||
|
c->fetch_sub(1, std::memory_order_release);
|
||||||
|
});
|
||||||
|
sem_.wait_if(std::forward<F>(check));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify(handle_t h) {
|
void notify(handle_t h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(evt_lc_);
|
sem_.post([this] {
|
||||||
evt_ids_.for_acquired([this](auto id) {
|
return (0 < counter_.load(std::memory_order_relaxed)) ? 1 : 0;
|
||||||
event evt { *static_cast<evt_id_t*>(evt_ids_.at(id)) };
|
|
||||||
return !evt.notify(); // return if succ
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void broadcast(handle_t h) {
|
void broadcast(handle_t h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(evt_lc_);
|
sem_.post([this] { return counter_.load(std::memory_order_relaxed); });
|
||||||
evt_ids_.for_acquired([this](auto id) {
|
|
||||||
event evt { *static_cast<evt_id_t*>(evt_ids_.at(id)) };
|
|
||||||
evt.notify();
|
|
||||||
return true; // return after all
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -37,10 +37,12 @@ public:
|
|||||||
::CloseHandle(h);
|
::CloseHandle(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool wait_all(std::tuple<waiter*, handle_t> const * all, std::size_t size) {
|
template <typename F>
|
||||||
|
static bool multi_wait_if(std::tuple<waiter*, handle_t> const * all, std::size_t size, F&& check) {
|
||||||
if (all == nullptr || size == 0) {
|
if (all == nullptr || size == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!std::forward<F>(check)()) return true;
|
||||||
auto hs = static_cast<handle_t*>(mem::alloc(sizeof(handle_t) * size));
|
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); });
|
IPC_UNUSED_ auto guard = unique_ptr(hs, [size](void* p) { mem::free(p, sizeof(handle_t) * size); });
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
@ -55,8 +57,10 @@ public:
|
|||||||
return ::WaitForMultipleObjects(static_cast<DWORD>(i), hs, FALSE, INFINITE) != WAIT_FAILED;
|
return ::WaitForMultipleObjects(static_cast<DWORD>(i), hs, FALSE, INFINITE) != WAIT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait(handle_t h) {
|
template <typename F>
|
||||||
|
bool wait_if(handle_t h, F&& check) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
|
if (!std::forward<F>(check)()) return true;
|
||||||
counter_.fetch_add(1, std::memory_order_relaxed);
|
counter_.fetch_add(1, std::memory_order_relaxed);
|
||||||
std::atomic_thread_fence(std::memory_order_release);
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
return ::WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0;
|
return ::WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0;
|
||||||
|
|||||||
@ -42,6 +42,7 @@ public:
|
|||||||
waiter_t const * waiter() const { return w_; }
|
waiter_t const * waiter() const { return w_; }
|
||||||
|
|
||||||
void attach(waiter_t* w) {
|
void attach(waiter_t* w) {
|
||||||
|
close();
|
||||||
w_ = w;
|
w_ = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +63,9 @@ public:
|
|||||||
h_ = waiter_t::invalid();
|
h_ = waiter_t::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait_all(waiter_wrapper * all, std::size_t size) {
|
|
||||||
|
template <typename F>
|
||||||
|
bool multi_wait_if(waiter_wrapper * all, std::size_t size, F&& check) {
|
||||||
if (all == nullptr || size == 0) {
|
if (all == nullptr || size == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -75,12 +78,13 @@ public:
|
|||||||
if (!w.valid()) continue;
|
if (!w.valid()) continue;
|
||||||
hs[i] = w.to_w_info();
|
hs[i] = w.to_w_info();
|
||||||
}
|
}
|
||||||
return waiter_t::wait_all(hs, i);
|
return waiter_t::multi_wait_if(hs, i, std::forward<F>(check));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait() {
|
template <typename F>
|
||||||
|
bool wait_if(F&& check) {
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
return w_->wait(h_);
|
return w_->wait_if(h_, std::forward<F>(check));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify() {
|
bool notify() {
|
||||||
|
|||||||
25
src/queue.h
25
src/queue.h
@ -110,7 +110,11 @@ public:
|
|||||||
bool wait_for_connect(Elems* elems, std::size_t count) {
|
bool wait_for_connect(Elems* elems, std::size_t count) {
|
||||||
if (elems == nullptr) return false;
|
if (elems == nullptr) return false;
|
||||||
for (unsigned k = 0; elems->conn_count() < count;) {
|
for (unsigned k = 0; elems->conn_count() < count;) {
|
||||||
ipc::sleep(k, [this] { return cc_waiter_.wait(); });
|
ipc::sleep(k, [this, elems, count] {
|
||||||
|
return cc_waiter_.wait_if([elems, count] {
|
||||||
|
return elems->conn_count() < count;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -214,13 +218,20 @@ public:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
T item;
|
T item;
|
||||||
for (unsigned k = 0;;) {
|
auto pop_item = [this, &item] {
|
||||||
if (elems_->pop(&this->cursor_, [&item](void* p) {
|
return elems_->pop(&this->cursor_, [&item](void* p) {
|
||||||
::new (&item) T(std::move(*static_cast<T*>(p)));
|
::new (&item) T(std::move(*static_cast<T*>(p)));
|
||||||
})) {
|
});
|
||||||
return item;
|
};
|
||||||
}
|
for (unsigned k = 0;;) {
|
||||||
ipc::sleep(k, [this] { return this->waiter_.wait(); });
|
if (pop_item()) return item;
|
||||||
|
bool succ = false;
|
||||||
|
ipc::sleep(k, [this, &succ, &pop_item] {
|
||||||
|
return this->waiter_.wait_if([&succ, &pop_item] {
|
||||||
|
return !(succ = pop_item());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (succ) return item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -64,7 +64,7 @@ void waiter::close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool waiter::wait() {
|
bool waiter::wait() {
|
||||||
return impl(p_)->w_.wait();
|
return impl(p_)->w_.wait_if([] { return true; });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waiter::notify() {
|
bool waiter::notify() {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "platform/waiter_wrapper.h"
|
#include "platform/waiter_wrapper.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
@ -13,34 +14,68 @@ class Unit : public TestSuite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void test_wakeup();
|
void test_broadcast();
|
||||||
|
void test_multiwait();
|
||||||
} unit__;
|
} unit__;
|
||||||
|
|
||||||
#include "test_waiter.moc"
|
#include "test_waiter.moc"
|
||||||
|
|
||||||
void Unit::test_wakeup() {
|
void Unit::test_broadcast() {
|
||||||
ipc::detail::waiter w;
|
ipc::detail::waiter w;
|
||||||
|
std::thread ts[10];
|
||||||
|
|
||||||
std::thread t1 {[&w] {
|
for (auto& t : ts) {
|
||||||
|
t = std::thread([&w] {
|
||||||
ipc::detail::waiter_wrapper wp { &w };
|
ipc::detail::waiter_wrapper wp { &w };
|
||||||
QVERIFY(wp.open("test-ipc-waiter"));
|
QVERIFY(wp.open("test-ipc-waiter"));
|
||||||
QVERIFY(wp.wait());
|
QVERIFY(wp.wait_if([] { return true; }));
|
||||||
}};
|
});
|
||||||
|
}
|
||||||
std::thread t2 {[&w] {
|
|
||||||
ipc::detail::waiter_wrapper wp { &w };
|
|
||||||
QVERIFY(wp.open("test-ipc-waiter"));
|
|
||||||
QVERIFY(wp.wait());
|
|
||||||
}};
|
|
||||||
|
|
||||||
ipc::detail::waiter_wrapper wp { &w };
|
ipc::detail::waiter_wrapper wp { &w };
|
||||||
QVERIFY(wp.open("test-ipc-waiter"));
|
QVERIFY(wp.open("test-ipc-waiter"));
|
||||||
|
|
||||||
|
std::cout << "waiting for broadcast...\n";
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
QVERIFY(wp.broadcast());
|
QVERIFY(wp.broadcast());
|
||||||
|
|
||||||
t1.join();
|
for (auto& t : ts) t.join();
|
||||||
t2.join();
|
}
|
||||||
|
|
||||||
|
void Unit::test_multiwait() {
|
||||||
|
ipc::detail::waiter w, mw;
|
||||||
|
std::thread ts[10];
|
||||||
|
ipc::detail::waiter_wrapper ws[10];
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (auto& t : ts) {
|
||||||
|
t = std::thread([&w, &mw, &ws, i] {
|
||||||
|
ipc::detail::waiter_wrapper wp { &w };
|
||||||
|
ws[i].attach(&mw);
|
||||||
|
|
||||||
|
QVERIFY(wp.open("test-ipc-waiter"));
|
||||||
|
QVERIFY(ws[i].open("test-ipc-multiwait"));
|
||||||
|
|
||||||
|
QVERIFY(wp.wait_if([] { return true; }));
|
||||||
|
if (i == 3) {
|
||||||
|
std::cout << "waiting for notify...\n";
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
QVERIFY(ws[i].broadcast());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc::detail::waiter_wrapper wp { &w }, wm { &mw };
|
||||||
|
QVERIFY(wp.open("test-ipc-waiter"));
|
||||||
|
QVERIFY(wm.open("test-ipc-multiwait"));
|
||||||
|
|
||||||
|
std::cout << "waiting for broadcast...\n";
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
QVERIFY(wp.broadcast());
|
||||||
|
QVERIFY(wm.multi_wait_if(ws, 10, [] { return true; }));
|
||||||
|
|
||||||
|
for (auto& t : ts) t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user