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 2088 : #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 65361 : fd_rwlock_new( fd_rwlock_t * lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 23 65361 : atomic_store_explicit( &lock->value, 0, memory_order_relaxed ); 24 65361 : return lock; 25 65361 : } 26 : 27 : static inline void 28 267338 : fd_rwlock_write( fd_rwlock_t * lock ) FD_ACQUIRE( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 29 267338 : for(;;) { 30 267333 : ushort value = atomic_load_explicit( &lock->value, memory_order_relaxed ); 31 267333 : fd_racesan_hook( "rwlock_write:pre_cas" ); 32 267336 : if( FD_LIKELY( !value ) ) { 33 267336 : ushort expected = 0; 34 267927 : if( FD_LIKELY( atomic_compare_exchange_weak_explicit( &lock->value, &expected, FD_RWLOCK_WRITE_LOCK, memory_order_acquire, memory_order_relaxed ) ) ) { 35 267927 : fd_racesan_hook( "rwlock_write:post_acquire" ); 36 267927 : return; 37 267927 : } 38 267336 : } 39 >1844*10^16 : FD_SPIN_PAUSE(); 40 >1844*10^16 : } 41 267338 : } 42 : 43 : static inline void 44 268043 : fd_rwlock_unwrite( fd_rwlock_t * lock ) FD_RELEASE( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 45 268043 : fd_racesan_hook( "rwlock_unwrite:pre_release" ); 46 268043 : atomic_store_explicit( &lock->value, 0, memory_order_release ); 47 268043 : } 48 : 49 : static inline void 50 0 : fd_rwlock_demote( fd_rwlock_t * lock ) FD_RELEASE( lock ) FD_ACQUIRE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 51 0 : fd_racesan_hook( "rwlock_demote:pre_release" ); 52 0 : atomic_store_explicit( &lock->value, 1, memory_order_release ); 53 0 : } 54 : 55 : static inline void 56 11330 : fd_rwlock_read( fd_rwlock_t * lock ) FD_ACQUIRE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 57 11330 : for(;;) { 58 11330 : ushort value = atomic_load_explicit( &lock->value, memory_order_relaxed ); 59 11330 : fd_racesan_hook( "rwlock_read:pre_cas" ); 60 11330 : if( FD_LIKELY( value<0xFFFE ) ) { 61 11330 : ushort expected = value; 62 11330 : if( FD_LIKELY( atomic_compare_exchange_weak_explicit( &lock->value, &expected, (ushort)(value+1), memory_order_acquire, memory_order_relaxed ) ) ) { 63 11327 : fd_racesan_hook( "rwlock_read:post_acquire" ); 64 11327 : return; 65 11327 : } 66 11330 : } 67 3 : FD_SPIN_PAUSE(); 68 3 : } 69 11330 : } 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 113 : fd_rwlock_tryread( fd_rwlock_t * lock ) FD_TRY_ACQUIRE_SHARED(1, lock) FD_NO_THREAD_SAFETY_ANALYSIS { 77 113 : ushort value = atomic_load_explicit( &lock->value, memory_order_relaxed ); 78 113 : fd_racesan_hook( "rwlock_tryread:pre_cas" ); 79 113 : if( FD_UNLIKELY( value>=0xFFFE ) ) return 0; 80 113 : ushort expected = value; 81 113 : if( FD_UNLIKELY( !atomic_compare_exchange_strong_explicit( &lock->value, &expected, (ushort)(value+1), memory_order_acquire, memory_order_relaxed ) ) ) return 0; 82 113 : return 1; 83 113 : } 84 : 85 : static inline int 86 4688 : fd_rwlock_trywrite( fd_rwlock_t * lock ) FD_TRY_ACQUIRE(1, lock) FD_NO_THREAD_SAFETY_ANALYSIS { 87 4688 : fd_racesan_hook( "rwlock_trywrite:pre_cas" ); 88 4688 : ushort expected = 0; 89 4688 : if( FD_UNLIKELY( !atomic_compare_exchange_strong_explicit( &lock->value, &expected, FD_RWLOCK_WRITE_LOCK, memory_order_acquire, memory_order_relaxed ) ) ) return 0; 90 4688 : return 1; 91 4688 : } 92 : 93 : static inline void 94 16127 : fd_rwlock_unread( fd_rwlock_t * lock ) FD_RELEASE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 95 16127 : fd_racesan_hook( "rwlock_unread:pre_release" ); 96 16127 : atomic_fetch_sub_explicit( &lock->value, 1, memory_order_release ); 97 16127 : } 98 : 99 : #endif /* HEADER_fd_src_flamenco_fd_rwlock_h */