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 : 10 : #define FD_RWLOCK_WRITE_LOCK ((ushort)0xFFFF) 11 : 12 : struct FD_CAPABILITY("fd_rwlock") fd_rwlock { 13 : ushort value; /* Bits 0..16 are 14 : 15 : 0: Unlocked 16 : 1..=0xFFFE: Locked by N readers 17 : 0xFFFF: Write locked */ 18 : }; 19 : 20 : typedef struct fd_rwlock fd_rwlock_t; 21 : 22 : static inline fd_rwlock_t * 23 109629 : fd_rwlock_new( fd_rwlock_t * lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 24 109629 : lock->value = 0; 25 109629 : return lock; 26 109629 : } 27 : 28 : static inline void 29 576414 : fd_rwlock_write( fd_rwlock_t * lock ) FD_ACQUIRE( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 30 576414 : # if FD_HAS_THREADS 31 576414 : for(;;) { 32 576414 : ushort value = lock->value; 33 576414 : fd_racesan_hook( "rwlock_write:pre_cas" ); 34 576414 : if( FD_LIKELY( !value ) ) { 35 576414 : if( FD_LIKELY( FD_ATOMIC_CAS( &lock->value, 0, 0xFFFF )==0 ) ) { 36 576414 : FD_COMPILER_MFENCE(); 37 576414 : fd_racesan_hook( "rwlock_write:post_acquire" ); 38 576414 : return; 39 576414 : } 40 576414 : } 41 0 : FD_SPIN_PAUSE(); 42 0 : } 43 : # else 44 : lock->value = 0xFFFF; 45 : FD_COMPILER_MFENCE(); 46 : # endif 47 576414 : } 48 : 49 : static inline void 50 576333 : fd_rwlock_unwrite( fd_rwlock_t * lock ) FD_RELEASE( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 51 576333 : fd_racesan_hook( "rwlock_unwrite:pre_release" ); 52 576333 : FD_COMPILER_MFENCE(); 53 576333 : FD_VOLATILE( lock->value ) = 0; 54 576333 : } 55 : 56 : static inline void 57 81 : fd_rwlock_demote( fd_rwlock_t * lock ) FD_RELEASE( lock ) FD_ACQUIRE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 58 81 : fd_racesan_hook( "rwlock_demote:pre_release" ); 59 81 : FD_COMPILER_MFENCE(); 60 81 : FD_VOLATILE( lock->value ) = 1; 61 81 : } 62 : 63 : static inline void 64 1236 : fd_rwlock_read( fd_rwlock_t * lock ) FD_ACQUIRE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 65 1236 : # if FD_HAS_THREADS 66 1236 : for(;;) { 67 1236 : ushort value = lock->value; 68 1236 : fd_racesan_hook( "rwlock_read:pre_cas" ); 69 1236 : if( FD_LIKELY( value<0xFFFE ) ) { 70 1236 : if( FD_LIKELY( FD_ATOMIC_CAS( &lock->value, value, value+1 )==value ) ) { 71 1236 : FD_COMPILER_MFENCE(); 72 1236 : fd_racesan_hook( "rwlock_read:post_acquire" ); 73 1236 : return; 74 1236 : } 75 1236 : } 76 0 : FD_SPIN_PAUSE(); 77 0 : } 78 : # else 79 : lock->value++; 80 : FD_COMPILER_MFENCE(); 81 : # endif 82 1236 : } 83 : 84 : /* fd_rwlock_tryread attempts to acquire a shared read lock without 85 : spinning. Returns 1 on success, 0 on failure (lock is write-held 86 : or contended). */ 87 : 88 : static inline int 89 54 : fd_rwlock_tryread( fd_rwlock_t * lock ) FD_TRY_ACQUIRE_SHARED(1, lock) FD_NO_THREAD_SAFETY_ANALYSIS { 90 54 : # if FD_HAS_THREADS 91 54 : ushort value = lock->value; 92 54 : fd_racesan_hook( "rwlock_tryread:pre_cas" ); 93 54 : if( FD_UNLIKELY( value>=0xFFFE ) ) return 0; 94 54 : if( FD_UNLIKELY( FD_ATOMIC_CAS( &lock->value, value, (ushort)(value+1) )!=value ) ) return 0; 95 54 : FD_COMPILER_MFENCE(); 96 54 : return 1; 97 : # else 98 : lock->value++; 99 : FD_COMPILER_MFENCE(); 100 : return 1; 101 : # endif 102 54 : } 103 : 104 : static inline int 105 0 : fd_rwlock_trywrite( fd_rwlock_t * lock ) FD_TRY_ACQUIRE(1, lock) FD_NO_THREAD_SAFETY_ANALYSIS { 106 0 : # if FD_HAS_THREADS 107 0 : fd_racesan_hook( "rwlock_trywrite:pre_cas" ); 108 0 : if( FD_UNLIKELY( FD_ATOMIC_CAS( &lock->value, 0, FD_RWLOCK_WRITE_LOCK )!=0 ) ) return 0; 109 0 : FD_COMPILER_MFENCE(); 110 0 : return 1; 111 0 : # else 112 0 : if( FD_UNLIKELY( lock->value ) ) return 0; 113 0 : lock->value = FD_RWLOCK_WRITE_LOCK; 114 0 : FD_COMPILER_MFENCE(); 115 0 : return 1; 116 0 : # endif 117 0 : } 118 : 119 : static inline void 120 1371 : fd_rwlock_unread( fd_rwlock_t * lock ) FD_RELEASE_SHARED( lock ) FD_NO_THREAD_SAFETY_ANALYSIS { 121 1371 : fd_racesan_hook( "rwlock_unread:pre_release" ); 122 1371 : FD_COMPILER_MFENCE(); 123 1371 : # if FD_HAS_THREADS 124 1371 : FD_ATOMIC_FETCH_AND_SUB( &lock->value, 1 ); 125 : # else 126 : lock->value--; 127 : # endif 128 1371 : } 129 : 130 : #endif /* HEADER_fd_src_flamenco_fd_rwlock_h */