diff --git a/CMakeLists.txt b/CMakeLists.txt index f8d2154..00ce953 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/src/init.c b/src/init.c index 78b3aab..04f72bc 100644 --- a/src/init.c +++ b/src/init.c @@ -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; diff --git a/src/reflock.c b/src/reflock.c index 474c6b7..e50355a 100644 --- a/src/reflock.c +++ b/src/reflock.c @@ -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); } diff --git a/src/reflock.h b/src/reflock.h index a3d276e..37da44b 100644 --- a/src/reflock.h +++ b/src/reflock.h @@ -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);