Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: neosmart/pevents
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a60ccc65d85b27820707a48cfa6f9a80a14e9f37
Choose a base ref
..
head repository: neosmart/pevents
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9c22e4e05383df9dc04ed3ed313c2300e077affa
Choose a head ref
Showing with 33 additions and 21 deletions.
  1. +33 −21 src/pevents.cpp
54 changes: 33 additions & 21 deletions src/pevents.cpp
Original file line number Diff line number Diff line change
@@ -301,7 +301,8 @@ namespace neosmart {
tempResult = pthread_mutex_unlock(&wfmo->Mutex);
assert(tempResult == 0);

int ref_count = wfmo->RefCount.fetch_sub(1 + skipped_refs, std::memory_order_acq_rel);
// memory_order_seq_cst: Ensure this is run after the wfmo mutex is unlocked
int ref_count = wfmo->RefCount.fetch_sub(1 + skipped_refs, std::memory_order_seq_cst);
assert(ref_count > 0);
if (ref_count == 1 + skipped_refs) {
wfmo->Destroy();
@@ -352,28 +353,36 @@ namespace neosmart {
// that point.
if (!i->Waiter->StillWaiting.load(std::memory_order_relaxed)) {
int ref_count = i->Waiter->RefCount.fetch_sub(1, std::memory_order_acq_rel);
assert(ref_count > 0);
if (ref_count == 1) {
i->Waiter->Destroy();
delete i->Waiter;
}

event->RegisteredWaits.pop_front();
continue;
}

result = pthread_mutex_lock(&i->Waiter->Mutex);
assert(result == 0);

int ref_count = i->Waiter->RefCount.fetch_sub(1, std::memory_order_acq_rel);
assert(ref_count > 0);
// memory_order_relaxed: this is only changed with the lock acquired
// We have to check `Waiter->StillWaiting` twice, once before locking as an
// optimization to bypass the mutex altogether, and then again after locking the
// WFMO mutex because we could have !waitAll and another event could have ended the
// wait, in which case we must not unlock the same waiter or else a SetEvent() call
// on an auto-reset event may end up with a lost wakeup.
if (!i->Waiter->StillWaiting.load(std::memory_order_relaxed)) {
result = pthread_mutex_unlock(&i->Waiter->Mutex);
assert(result == 0);

// memory_order_seq_cst: Ensure this is run after the wfmo mutex is unlocked
int ref_count = i->Waiter->RefCount.fetch_sub(1, std::memory_order_seq_cst);
assert(ref_count > 0);
if (ref_count == 1) {
i->Waiter->Destroy();
delete i->Waiter;
}

event->RegisteredWaits.pop_front();
continue;
}
@@ -398,12 +407,15 @@ namespace neosmart {
result = pthread_cond_signal(&i->Waiter->CVariable);
assert(result == 0);

event->RegisteredWaits.pop_front();
// memory_order_seq_cst: Ensure this is run after the wfmo mutex is unlocked
int ref_count = i->Waiter->RefCount.fetch_sub(1, std::memory_order_seq_cst);
assert(ref_count > 0);
if (ref_count == 1) {
i->Waiter->Destroy();
delete i->Waiter;
}

// The auto-reset event has been consumed, so we must make sure it's no longer set.
// memory_order_relaxed: `State` is only set to true with the mutex held, and we
// require that this function only be called after the mutex is obtained.
event->State.store(false, std::memory_order_relaxed);
event->RegisteredWaits.pop_front();

result = pthread_mutex_unlock(&event->Mutex);
assert(result == 0);
@@ -445,18 +457,9 @@ namespace neosmart {
result = pthread_mutex_lock(&info->Waiter->Mutex);
assert(result == 0);

int ref_count = info->Waiter->RefCount.fetch_sub(1, std::memory_order_acq_rel);
assert(ref_count > 0);

if (!info->Waiter->StillWaiting) {
result = pthread_mutex_unlock(&info->Waiter->Mutex);
assert(result == 0);
if (ref_count == 1) {
info->Waiter->Destroy();
delete info->Waiter;
}
continue;
}
// Waiter->StillWaiting may have become true by now, but we're just going to pretend
// it hasn't. So long as we hold a reference to the WFMO, this is safe since manual
// reset events are not one-time use.

if (info->Waiter->WaitAll) {
--info->Waiter->Status.EventsLeft;
@@ -477,6 +480,15 @@ namespace neosmart {

result = pthread_cond_signal(&info->Waiter->CVariable);
assert(result == 0);

// memory_order_seq_cst: Ensure this is run after the wfmo mutex is unlocked
int ref_count = info->Waiter->RefCount.fetch_sub(1, std::memory_order_seq_cst);
assert(ref_count > 0);
if (ref_count == 1) {
info->Waiter->Destroy();
delete info->Waiter;
}
continue;
}
event->RegisteredWaits.clear();
#endif // WFMO