this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

libsimple: Implement condvars

+102 -2
+23
src/libsimple/include/libsimple/lock.h
··· 69 69 bool libsimple_rwlock_try_lock_write(libsimple_rwlock_t* rwlock); 70 70 void libsimple_rwlock_unlock_write(libsimple_rwlock_t* rwlock); 71 71 72 + // 73 + // condvar 74 + // 75 + 76 + typedef struct libsimple_condvar { 77 + uint32_t state; 78 + } libsimple_condvar_t; 79 + 80 + #define LIBSIMPLE_CONDVAR_INITIALIZER {0} 81 + 82 + LIBSIMPLE_INLINE 83 + static void libsimple_condvar_init(libsimple_condvar_t* condvar) { 84 + condvar->state = 0; 85 + }; 86 + 87 + // NOTE: the same lock must be used for all operations with a condvar. 88 + // the only reason we don't just store a pointer to the lock in the condvar 89 + // is so that condvars can be initialized statically. 90 + 91 + void libsimple_condvar_wait(libsimple_condvar_t* condvar, libsimple_lock_t* lock); 92 + void libsimple_condvar_notify_one(libsimple_condvar_t* condvar, libsimple_lock_t* lock); 93 + void libsimple_condvar_notify_all(libsimple_condvar_t* condvar, libsimple_lock_t* lock); 94 + 72 95 LIBSIMPLE_DECLARATIONS_END; 73 96 74 97 #endif // _LIBSIMPLE_LOCK_H_
+79 -2
src/libsimple/src/lock.c
··· 21 21 22 22 #define FUTEX_WAIT 0 23 23 #define FUTEX_WAKE 1 24 + #define FUTEX_REQUEUE 3 24 25 #define FUTEX_WAIT_BITSET 9 25 26 #define FUTEX_WAKE_BITSET 10 26 27 #define FUTEX_PRIVATE_FLAG 128 ··· 37 38 #if LIBSIMPLE_DARLING 38 39 #define linux_futex __linux_futex_reterr 39 40 #else // !LIBSIMPLE_DARLING 40 - static void linux_futex(int* uaddr, int op, int val, const struct timespec* timeout, int* uaddr2, int val3) { 41 + static int linux_futex(int* uaddr, int op, int val, const struct timespec* timeout, int* uaddr2, int val3) { 41 42 #if LIBSIMPLE_LINUX 42 - syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); 43 + return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); 43 44 #else 44 45 #error linux_futex not implemented for this platform 45 46 #endif ··· 108 109 } 109 110 110 111 libsimple_lock_debug("lock acquired"); 112 + }; 113 + 114 + // based on Mutex::lock_pessimistic from https://github.com/bugaevc/lets-write-sync-primitives/blob/master/src/mutex.cpp 115 + // 116 + // this is used by condvars for locking the lock after waking up 117 + static void libsimple_lock_lock_slow(libsimple_lock_t* _lock) { 118 + libsimple_lock_internal_t* lock = (libsimple_lock_internal_t*)_lock; 119 + 120 + // this path assumes that the lock is contended 121 + 122 + uint32_t prev = atomic_exchange_explicit(&lock->state, libsimple_lock_state_locked_contended, memory_order_acquire); 123 + 124 + while (prev != libsimple_lock_state_unlocked) { 125 + linux_futex((int*)&lock->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, libsimple_lock_state_locked_contended, NULL, 0, 0); 126 + prev = atomic_exchange_explicit(&lock->state, libsimple_lock_state_locked_contended, memory_order_acquire); 127 + } 111 128 }; 112 129 113 130 void libsimple_lock_unlock(libsimple_lock_t* _lock) { ··· 372 389 linux_futex((int*)&rwlock->state, FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, libsimple_rwlock_bit_writer); 373 390 } 374 391 }; 392 + 393 + // 394 + // libsimple_condvar 395 + // 396 + // based on https://github.com/bugaevc/lets-write-sync-primitives/blob/master/src/condvar.cpp 397 + // 398 + 399 + typedef struct libsimple_condvar_internal { 400 + _Atomic uint32_t state; 401 + } libsimple_condvar_internal_t; 402 + 403 + enum libsimple_condvar_bits { 404 + libsimple_condvar_bit_need_to_wake_one = 1 << 0, 405 + libsimple_condvar_bit_need_to_wake_all = 1 << 1, 406 + libsimple_condvar_bit_increment = 1 << 2, 407 + }; 408 + 409 + void libsimple_condvar_wait(libsimple_condvar_t* _condvar, libsimple_lock_t* lock) { 410 + libsimple_condvar_internal_t* condvar = (void*)_condvar; 411 + uint32_t state2 = atomic_fetch_or_explicit(&condvar->state, libsimple_condvar_bit_need_to_wake_all | libsimple_condvar_bit_need_to_wake_one, memory_order_relaxed) | libsimple_condvar_bit_need_to_wake_all | libsimple_condvar_bit_need_to_wake_one; 412 + libsimple_lock_unlock(lock); 413 + linux_futex((int*)&condvar->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, state2, NULL, NULL, 0); 414 + libsimple_lock_lock_slow(lock); 415 + }; 416 + 417 + void libsimple_condvar_notify_one(libsimple_condvar_t* _condvar, libsimple_lock_t* lock) { 418 + libsimple_condvar_internal_t* condvar = (void*)_condvar; 419 + uint32_t state2 = atomic_fetch_add_explicit(&condvar->state, libsimple_condvar_bit_increment, memory_order_relaxed) + libsimple_condvar_bit_increment; 420 + int woken; 421 + 422 + if ((state2 & libsimple_condvar_bit_need_to_wake_one) == 0) { 423 + return; 424 + } 425 + 426 + state2 = atomic_fetch_and_explicit(&condvar->state, ~libsimple_condvar_bit_need_to_wake_one, memory_order_relaxed); 427 + 428 + if ((state2 & libsimple_condvar_bit_need_to_wake_one) == 0) { 429 + return; 430 + } 431 + 432 + woken = linux_futex((int*)&condvar->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0); 433 + 434 + if (woken > 0) { 435 + atomic_fetch_or_explicit(&condvar->state, libsimple_condvar_bit_need_to_wake_one, memory_order_relaxed); 436 + } 437 + }; 438 + 439 + void libsimple_condvar_notify_all(libsimple_condvar_t* _condvar, libsimple_lock_t* _lock) { 440 + libsimple_condvar_internal_t* condvar = (void*)_condvar; 441 + libsimple_lock_internal_t* lock = (void*)_lock; 442 + uint32_t state2 = atomic_fetch_add_explicit(&condvar->state, libsimple_condvar_bit_increment, memory_order_relaxed) + libsimple_condvar_bit_increment; 443 + 444 + if ((state2 & libsimple_condvar_bit_need_to_wake_all) == 0) { 445 + return; 446 + } 447 + 448 + atomic_fetch_and_explicit(&condvar->state, ~(libsimple_condvar_bit_need_to_wake_all | libsimple_condvar_bit_need_to_wake_one), memory_order_relaxed); 449 + 450 + linux_futex((int*)&condvar->state, FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG, 1, /* actually `uint32_t val2`, not timeout */ (void*)INT_MAX, (int*)&lock->state, 0); 451 + };