Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_fd_rwlock_h 2 : #define HEADER_fd_src_flamenco_fd_rwlock_h 3 : 4 : /* A very simple read-write spin lock. */ 5 : 6 : #include "../util/fd_util_base.h" 7 : #include "../util/sanitize/fd_tsa.h" 8 : #include "../util/racesan/fd_racesan_target.h" 9 : #include <stdatomic.h> 10 : 11 : #define FD_RWLOCK_WRITE_LOCK ((ushort)0xFFFF) 12 : 13 : struct FD_CAPABILITY("fd_rwlock") fd_rwlock { 14 : atomic_ushort value; /* 0: Unlocked 15 : 1..=0xFFFE: Locked by N readers 16 : 0xFFFF: Write locked */ 17 : }; 18 : 19 : typedef struct fd_rwlock fd_rwlock_t; 20 : 21 : static inline fd_rwlock_t * 22 4989 : fd_rwlock_new( fd_rwlock_t * lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 23 4989 : atomic_store_explicit( &lock->value, 0, memory_order_relaxed ); 24 4989 : return lock; 25 4989 : } 26 : 27 : static inline void 28 72432 : fd_rwlock_write( fd_rwlock_t * lock ) FD_ACQUIRE( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 29 72432 : for(;;) { 30 72432 : ushort value = atomic_load_explicit( &lock->value, memory_order_relaxed ); 31 72432 : fd_racesan_hook( "rwlock_write:pre_cas" ); 32 72432 : if( FD_LIKELY( !value ) ) { 33 72432 : ushort expected = 0; 34 72432 : if( FD_LIKELY( atomic_compare_exchange_weak_explicit( &lock->value, &expected, FD_RWLOCK_WRITE_LOCK, memory_order_acquire, memory_order_relaxed ) ) ) { 35 72432 : fd_racesan_hook( "rwlock_write:post_acquire" ); 36 72432 : return; 37 72432 : } 38 72432 : } 39 0 : FD_SPIN_PAUSE(); 40 0 : } 41 72432 : } 42 : 43 : static inline void 44 72327 : fd_rwlock_unwrite( fd_rwlock_t * lock ) FD_RELEASE( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 45 72327 : fd_racesan_hook( "rwlock_unwrite:pre_release" ); 46 72327 : atomic_store_explicit( &lock->value, 0, memory_order_release ); 47 72327 : } 48 : 49 : static inline void 50 111 : fd_rwlock_demote( fd_rwlock_t * lock ) FD_RELEASE( lock ) FD_ACQUIRE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 51 111 : fd_racesan_hook( "rwlock_demote:pre_release" ); 52 111 : atomic_store_explicit( &lock->value, 1, memory_order_release ); 53 111 : } 54 : 55 : static inline void 56 5424 : fd_rwlock_read( fd_rwlock_t * lock ) FD_ACQUIRE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 57 5424 : for(;;) { 58 5424 : ushort value = atomic_load_explicit( &lock->value, memory_order_relaxed ); 59 5424 : fd_racesan_hook( "rwlock_read:pre_cas" ); 60 5424 : if( FD_LIKELY( value<0xFFFE ) ) { 61 5424 : ushort expected = value; 62 5424 : if( FD_LIKELY( atomic_compare_exchange_weak_explicit( &lock->value, &expected, (ushort)(value+1), memory_order_acquire, memory_order_relaxed ) ) ) { 63 5424 : fd_racesan_hook( "rwlock_read:post_acquire" ); 64 5424 : return; 65 5424 : } 66 5424 : } 67 0 : FD_SPIN_PAUSE(); 68 0 : } 69 5424 : } 70 : 71 : /* fd_rwlock_tryread attempts to acquire a shared read lock without 72 : spinning. Returns 1 on success, 0 on failure (lock is write-held 73 : or contended). */ 74 : 75 : static inline int 76 66 : fd_rwlock_tryread( fd_rwlock_t * lock ) FD_TRY_ACQUIRE_SHARED(1, lock) FD_NO_THREAD_SAFETY_ANALYSIS { 77 66 : ushort value = atomic_load_explicit( &lock->value, memory_order_relaxed ); 78 66 : fd_racesan_hook( "rwlock_tryread:pre_cas" ); 79 66 : if( FD_UNLIKELY( value>=0xFFFE ) ) return 0; 80 63 : ushort expected = value; 81 63 : if( FD_UNLIKELY( !atomic_compare_exchange_strong_explicit( &lock->value, &expected, (ushort)(value+1), memory_order_acquire, memory_order_relaxed ) ) ) return 0; 82 63 : return 1; 83 63 : } 84 : 85 : static inline int 86 114 : fd_rwlock_trywrite( fd_rwlock_t * lock ) FD_TRY_ACQUIRE(1, lock) FD_NO_THREAD_SAFETY_ANALYSIS { 87 114 : fd_racesan_hook( "rwlock_trywrite:pre_cas" ); 88 114 : ushort expected = 0; 89 114 : if( FD_UNLIKELY( !atomic_compare_exchange_strong_explicit( &lock->value, &expected, FD_RWLOCK_WRITE_LOCK, memory_order_acquire, memory_order_relaxed ) ) ) return 0; 90 102 : return 1; 91 114 : } 92 : 93 : static inline void 94 8052 : fd_rwlock_unread( fd_rwlock_t * lock ) FD_RELEASE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 95 8052 : fd_racesan_hook( "rwlock_unread:pre_release" ); 96 : atomic_fetch_sub_explicit( &lock->value, 1, memory_order_release ); 97 8052 : } 98 : 99 : #endif /* HEADER_fd_src_flamenco_fd_rwlock_h */