Switch to condition variables

This commit is contained in:
Raymond Zhao 2024-07-22 15:43:34 -07:00
parent c265e8625b
commit 48de2a9598
No known key found for this signature in database
GPG Key ID: 4DA8A81F5F7FCF93
4 changed files with 46 additions and 13 deletions

View File

@ -4,7 +4,6 @@ project(wepoll)
include(CMakeParseArguments)
link_libraries(ws2_32)
link_libraries(synchronization)
if(MSVC)
add_compile_options(/Wall /WX /wd4127 /wd4191 /wd4201 /wd4242 /wd4710 /wd4711 /wd4820)

View File

@ -21,7 +21,7 @@ static BOOL CALLBACK init__once_callback(INIT_ONCE* once,
/* N.b. that initialization order matters here. */
if (ws_global_init() < 0 || nt_global_init() < 0 ||
epoll_global_init() < 0)
reflock_global_init() < 0 || epoll_global_init() < 0)
return FALSE;
init__done = true;

View File

@ -14,31 +14,58 @@
#define REFLOCK__DESTROY_MASK ((long) 0x10000000UL)
#define REFLOCK__SIGNAL ((long) 0x20000000UL)
#define REFLOCK__SIGNAL_MASK ((long) 0x20000000UL)
#define REFLOCK__POISON ((long) 0x300dead0UL)
#define REFLOCK__AWAIT ((long) 0x40000000UL)
#define REFLOCK__AWAIT_MASK ((long) 0x40000000UL)
#define REFLOCK__POISON ((long) 0x800dead0UL)
/* clang-format on */
static CRITICAL_SECTION signalMutex;
int reflock_global_init(void) {
InitializeCriticalSection(&signalMutex);
return 0;
}
void reflock_init(reflock_t* reflock) {
reflock->state = 0;
InitializeConditionVariable(&reflock->cv_signal);
InitializeConditionVariable(&reflock->cv_await);
}
static void reflock__signal_event(reflock_t* reflock) {
long state = InterlockedAdd(&reflock->state, REFLOCK__SIGNAL);
unused_var(state);
BOOL status = TRUE;
WakeByAddressSingle(reflock);
EnterCriticalSection(&signalMutex);
long state = InterlockedOr(&reflock->state, REFLOCK__SIGNAL);
while ((reflock->state & REFLOCK__AWAIT_MASK) == 0) {
status = SleepConditionVariableCS(&reflock->cv_signal, &signalMutex, INFINITE);
}
LeaveCriticalSection(&signalMutex);
if (status != TRUE)
abort();
/* At most one reflock__await_event call per reflock. */
WakeConditionVariable(&reflock->cv_await);
unused_var(state);
}
static void reflock__await_event(reflock_t* reflock) {
BOOL status = TRUE;
do {
status = WaitOnAddress(reflock, reflock, sizeof(reflock_t*), INFINITE);
} while ((reflock->state & REFLOCK__SIGNAL_MASK) == 0);
long state = InterlockedAdd(&reflock->state, -REFLOCK__SIGNAL);
unused_var(state);
EnterCriticalSection(&signalMutex);
long state = InterlockedOr(&reflock->state, REFLOCK__AWAIT);
while ((reflock->state & REFLOCK__SIGNAL_MASK) == 0) {
status = SleepConditionVariableCS(&reflock->cv_await, &signalMutex, INFINITE);
}
LeaveCriticalSection(&signalMutex);
if (status != TRUE)
abort();
/* Multiple threads could be waiting. */
WakeAllConditionVariable(&reflock->cv_signal);
unused_var(state);
}
void reflock_ref(reflock_t* reflock) {
@ -55,7 +82,8 @@ void reflock_unref(reflock_t* reflock) {
/* Verify that the lock was referenced and not already destroyed. */
assert((state & REFLOCK__DESTROY_MASK & ~REFLOCK__DESTROY) == 0);
if (state == REFLOCK__DESTROY)
if ((state & REFLOCK__DESTROY_MASK) == REFLOCK__DESTROY &&
(state & REFLOCK__REF_MASK) == 0)
reflock__signal_event(reflock);
}
@ -71,5 +99,6 @@ void reflock_unref_and_destroy(reflock_t* reflock) {
reflock__await_event(reflock);
state = InterlockedExchange(&reflock->state, REFLOCK__POISON);
assert(state == REFLOCK__DESTROY);
assert((state & REFLOCK__DESTROY_MASK) == REFLOCK__DESTROY);
assert((state & REFLOCK__REF_MASK) == 0);
}

View File

@ -2,6 +2,7 @@
#define WEPOLL_REFLOCK_H_
#include "config.h"
#include "win.h"
/* A reflock is a special kind of lock that normally prevents a chunk of
* memory from being freed, but does allow the chunk of memory to eventually be
@ -23,8 +24,12 @@
typedef struct reflock {
volatile long state; /* 32-bit Interlocked APIs operate on `long` values. */
CONDITION_VARIABLE cv_signal;
CONDITION_VARIABLE cv_await;
} reflock_t;
WEPOLL_INTERNAL int reflock_global_init(void);
WEPOLL_INTERNAL void reflock_init(reflock_t* reflock);
WEPOLL_INTERNAL void reflock_ref(reflock_t* reflock);
WEPOLL_INTERNAL void reflock_unref(reflock_t* reflock);