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