| [ab1b971] | 1 | //
 | 
|---|
 | 2 | // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
 | 
|---|
 | 3 | //
 | 
|---|
 | 4 | // The contents of this file are covered under the licence agreement in the
 | 
|---|
 | 5 | // file "LICENCE" distributed with Cforall.
 | 
|---|
 | 6 | //
 | 
|---|
 | 7 | // locks.hfa -- PUBLIC
 | 
|---|
 | 8 | // Runtime locks that used with the runtime thread system.
 | 
|---|
 | 9 | //
 | 
|---|
 | 10 | // Author           : Colby Alexander Parsons
 | 
|---|
 | 11 | // Created On       : Thu Jan 21 19:46:50 2021
 | 
|---|
 | 12 | // Last Modified By :
 | 
|---|
 | 13 | // Last Modified On :
 | 
|---|
 | 14 | // Update Count     :
 | 
|---|
 | 15 | //
 | 
|---|
 | 16 | 
 | 
|---|
| [f4e35326] | 17 | #pragma once
 | 
|---|
 | 18 | 
 | 
|---|
| [848439f] | 19 | #include <stdbool.h>
 | 
|---|
| [5a46e09] | 20 | #include <stdio.h>
 | 
|---|
| [848439f] | 21 | 
 | 
|---|
| [ab1b971] | 22 | #include "bits/weakso_locks.hfa"
 | 
|---|
| [55b060d] | 23 | #include "collections/lockfree.hfa"
 | 
|---|
 | 24 | #include "collections/list.hfa"
 | 
|---|
| [f4ec5e45] | 25 | 
 | 
|---|
| [07033ce] | 26 | #include "limits.hfa"
 | 
|---|
| [f4ec5e45] | 27 | #include "thread.hfa"
 | 
|---|
| [848439f] | 28 | 
 | 
|---|
 | 29 | #include "time_t.hfa"
 | 
|---|
 | 30 | #include "time.hfa"
 | 
|---|
 | 31 | 
 | 
|---|
| [beeff61e] | 32 | #include "select.hfa"
 | 
|---|
 | 33 | 
 | 
|---|
| [b77f0e1] | 34 | // futex headers
 | 
|---|
 | 35 | #include <linux/futex.h>      /* Definition of FUTEX_* constants */
 | 
|---|
 | 36 | #include <sys/syscall.h>      /* Definition of SYS_* constants */
 | 
|---|
| [1e538fb] | 37 | #include <unistd.h>           /* Definition of syscall routine */
 | 
|---|
| [b77f0e1] | 38 | 
 | 
|---|
| [fece3d9] | 39 | typedef void (*__cfa_pre_park)( void * );
 | 
|---|
 | 40 | 
 | 
|---|
 | 41 | static inline void pre_park_noop( void * ) {}
 | 
|---|
 | 42 | 
 | 
|---|
 | 43 | //-----------------------------------------------------------------------------
 | 
|---|
 | 44 | // is_blocking_lock
 | 
|---|
 | 45 | forall( L & | sized(L) )
 | 
|---|
 | 46 | trait is_blocking_lock {
 | 
|---|
 | 47 |         // For synchronization locks to use when acquiring
 | 
|---|
 | 48 |         void on_notify( L &, struct thread$ * );
 | 
|---|
 | 49 | 
 | 
|---|
 | 50 |         // For synchronization locks to use when releasing
 | 
|---|
 | 51 |         size_t on_wait( L &, __cfa_pre_park pp_fn, void * pp_datum );
 | 
|---|
 | 52 | 
 | 
|---|
 | 53 |         // to set recursion count after getting signalled;
 | 
|---|
 | 54 |         void on_wakeup( L &, size_t recursion );
 | 
|---|
 | 55 | };
 | 
|---|
 | 56 | 
 | 
|---|
 | 57 | static inline void pre_park_then_park( __cfa_pre_park pp_fn, void * pp_datum ) {
 | 
|---|
 | 58 |     pp_fn( pp_datum );
 | 
|---|
 | 59 |     park();
 | 
|---|
 | 60 | }
 | 
|---|
 | 61 | 
 | 
|---|
| [5a05946] | 62 | // macros for default routine impls for is_blocking_lock trait that do not wait-morph
 | 
|---|
 | 63 | 
 | 
|---|
 | 64 | #define DEFAULT_ON_NOTIFY( lock_type ) \
 | 
|---|
 | 65 |     static inline void on_notify( lock_type & this, thread$ * t ){ unpark(t); }
 | 
|---|
 | 66 | 
 | 
|---|
 | 67 | #define DEFAULT_ON_WAIT( lock_type ) \
 | 
|---|
 | 68 |     static inline size_t on_wait( lock_type & this, __cfa_pre_park pp_fn, void * pp_datum ) { \
 | 
|---|
 | 69 |         unlock( this ); \
 | 
|---|
 | 70 |         pre_park_then_park( pp_fn, pp_datum ); \
 | 
|---|
 | 71 |         return 0; \
 | 
|---|
 | 72 |     }
 | 
|---|
 | 73 | 
 | 
|---|
 | 74 | // on_wakeup impl if lock should be reacquired after waking up
 | 
|---|
 | 75 | #define DEFAULT_ON_WAKEUP_REACQ( lock_type ) \
 | 
|---|
 | 76 |     static inline void on_wakeup( lock_type & this, size_t recursion ) { lock( this ); }
 | 
|---|
 | 77 | 
 | 
|---|
 | 78 | // on_wakeup impl if lock will not be reacquired after waking up
 | 
|---|
 | 79 | #define DEFAULT_ON_WAKEUP_NO_REACQ( lock_type ) \
 | 
|---|
 | 80 |     static inline void on_wakeup( lock_type & this, size_t recursion ) {}
 | 
|---|
 | 81 | 
 | 
|---|
 | 82 | 
 | 
|---|
 | 83 | 
 | 
|---|
| [f4ec5e45] | 84 | //-----------------------------------------------------------------------------
 | 
|---|
 | 85 | // Semaphore
 | 
|---|
 | 86 | struct semaphore {
 | 
|---|
 | 87 |         __spinlock_t lock;
 | 
|---|
 | 88 |         int count;
 | 
|---|
| [e84ab3d] | 89 |         __queue_t(thread$) waiting;
 | 
|---|
| [f4ec5e45] | 90 | };
 | 
|---|
 | 91 | 
 | 
|---|
 | 92 | void  ?{}(semaphore & this, int count = 1);
 | 
|---|
 | 93 | void ^?{}(semaphore & this);
 | 
|---|
 | 94 | bool   P (semaphore & this);
 | 
|---|
 | 95 | bool   V (semaphore & this);
 | 
|---|
 | 96 | bool   V (semaphore & this, unsigned count);
 | 
|---|
| [e84ab3d] | 97 | thread$ * V (semaphore & this, bool );
 | 
|---|
| [f4ec5e45] | 98 | 
 | 
|---|
| [ab1b971] | 99 | //----------
 | 
|---|
 | 100 | struct single_acquisition_lock {
 | 
|---|
 | 101 |         inline blocking_lock;
 | 
|---|
 | 102 | };
 | 
|---|
 | 103 | 
 | 
|---|
 | 104 | static inline void  ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
 | 
|---|
 | 105 | static inline void ^?{}( single_acquisition_lock & this ) {}
 | 
|---|
| [22b7579] | 106 | static inline void   lock     ( single_acquisition_lock & this ) { lock    ( (blocking_lock &)this ); }
 | 
|---|
 | 107 | static inline bool   try_lock ( single_acquisition_lock & this ) { return try_lock( (blocking_lock &)this ); }
 | 
|---|
 | 108 | static inline void   unlock   ( single_acquisition_lock & this ) { unlock  ( (blocking_lock &)this ); }
 | 
|---|
| [fece3d9] | 109 | static inline size_t on_wait  ( single_acquisition_lock & this, __cfa_pre_park pp_fn, void * pp_datum ) { return on_wait ( (blocking_lock &)this, pp_fn, pp_datum ); }
 | 
|---|
| [22b7579] | 110 | static inline void   on_wakeup( single_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
 | 
|---|
| [e84ab3d] | 111 | static inline void   on_notify( single_acquisition_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); }
 | 
|---|
| [beeff61e] | 112 | static inline bool   register_select( single_acquisition_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
 | 
|---|
 | 113 | static inline bool   unregister_select( single_acquisition_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
 | 
|---|
| [b93bf85] | 114 | static inline bool   on_selected( single_acquisition_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }
 | 
|---|
| [bf55f32] | 115 | __CFA_SELECT_GET_TYPE( single_acquisition_lock );
 | 
|---|
| [ab1b971] | 116 | 
 | 
|---|
 | 117 | //----------
 | 
|---|
 | 118 | struct owner_lock {
 | 
|---|
 | 119 |         inline blocking_lock;
 | 
|---|
 | 120 | };
 | 
|---|
 | 121 | 
 | 
|---|
 | 122 | static inline void  ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
 | 
|---|
 | 123 | static inline void ^?{}( owner_lock & this ) {}
 | 
|---|
| [f19497c] | 124 | static inline void   lock     ( owner_lock & this ) { lock    ( (blocking_lock &)this ); }
 | 
|---|
| [d27b6be] | 125 | static inline bool   try_lock ( owner_lock & this ) { return try_lock( (blocking_lock &)this ); }
 | 
|---|
| [f19497c] | 126 | static inline void   unlock   ( owner_lock & this ) { unlock  ( (blocking_lock &)this ); }
 | 
|---|
| [fece3d9] | 127 | static inline size_t on_wait  ( owner_lock & this, __cfa_pre_park pp_fn, void * pp_datum ) { return on_wait ( (blocking_lock &)this, pp_fn, pp_datum ); }
 | 
|---|
| [22b7579] | 128 | static inline void   on_wakeup( owner_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
 | 
|---|
| [e84ab3d] | 129 | static inline void   on_notify( owner_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); }
 | 
|---|
| [beeff61e] | 130 | static inline bool   register_select( owner_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
 | 
|---|
 | 131 | static inline bool   unregister_select( owner_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
 | 
|---|
| [b93bf85] | 132 | static inline bool   on_selected( owner_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }
 | 
|---|
| [bf55f32] | 133 | __CFA_SELECT_GET_TYPE( owner_lock );
 | 
|---|
| [ab1b971] | 134 | 
 | 
|---|
| [7f958c4] | 135 | //-----------------------------------------------------------------------------
 | 
|---|
 | 136 | // MCS Lock
 | 
|---|
| [f4ec5e45] | 137 | struct mcs_node {
 | 
|---|
 | 138 |         mcs_node * volatile next;
 | 
|---|
 | 139 |         single_sem sem;
 | 
|---|
 | 140 | };
 | 
|---|
 | 141 | 
 | 
|---|
| [7a2c6b18] | 142 | static inline void ?{}( mcs_node & this ) { this.next = 0p; }
 | 
|---|
| [f4ec5e45] | 143 | 
 | 
|---|
 | 144 | static inline mcs_node * volatile & ?`next ( mcs_node * node ) {
 | 
|---|
 | 145 |         return node->next;
 | 
|---|
 | 146 | }
 | 
|---|
 | 147 | 
 | 
|---|
 | 148 | struct mcs_lock {
 | 
|---|
 | 149 |         mcs_queue(mcs_node) queue;
 | 
|---|
 | 150 | };
 | 
|---|
 | 151 | 
 | 
|---|
| [7a2c6b18] | 152 | static inline void lock( mcs_lock & l, mcs_node & n ) {
 | 
|---|
| [f4ec5e45] | 153 |         if(push(l.queue, &n))
 | 
|---|
 | 154 |                 wait(n.sem);
 | 
|---|
 | 155 | }
 | 
|---|
 | 156 | 
 | 
|---|
 | 157 | static inline void unlock(mcs_lock & l, mcs_node & n) {
 | 
|---|
 | 158 |         mcs_node * next = advance(l.queue, &n);
 | 
|---|
 | 159 |         if(next) post(next->sem);
 | 
|---|
 | 160 | }
 | 
|---|
 | 161 | 
 | 
|---|
| [f835806] | 162 | //-----------------------------------------------------------------------------
 | 
|---|
 | 163 | // MCS Spin Lock
 | 
|---|
 | 164 | // - No recursive acquisition
 | 
|---|
 | 165 | // - Needs to be released by owner
 | 
|---|
 | 166 | 
 | 
|---|
 | 167 | struct mcs_spin_node {
 | 
|---|
 | 168 |         mcs_spin_node * volatile next;
 | 
|---|
| [db7a3ad] | 169 |         volatile bool locked;
 | 
|---|
| [f835806] | 170 | };
 | 
|---|
 | 171 | 
 | 
|---|
 | 172 | struct mcs_spin_queue {
 | 
|---|
 | 173 |         mcs_spin_node * volatile tail;
 | 
|---|
 | 174 | };
 | 
|---|
 | 175 | 
 | 
|---|
| [7a2c6b18] | 176 | static inline void ?{}( mcs_spin_node & this ) { this.next = 0p; this.locked = true; }
 | 
|---|
| [f835806] | 177 | 
 | 
|---|
 | 178 | struct mcs_spin_lock {
 | 
|---|
 | 179 |         mcs_spin_queue queue;
 | 
|---|
 | 180 | };
 | 
|---|
 | 181 | 
 | 
|---|
| [7a2c6b18] | 182 | static inline void lock( mcs_spin_lock & l, mcs_spin_node & n ) {
 | 
|---|
| [5ece8ce] | 183 |     n.locked = true;
 | 
|---|
| [8df19af] | 184 | 
 | 
|---|
 | 185 |         #if defined(__ARM_ARCH)
 | 
|---|
| [2ad5e1d5] | 186 |         __asm__ __volatile__ ( "DMB ISH" ::: );
 | 
|---|
| [8df19af] | 187 |         #endif
 | 
|---|
 | 188 | 
 | 
|---|
| [f835806] | 189 |         mcs_spin_node * prev = __atomic_exchange_n(&l.queue.tail, &n, __ATOMIC_SEQ_CST);
 | 
|---|
| [5ece8ce] | 190 |         if( prev == 0p ) return;
 | 
|---|
| [9e3d123] | 191 |         prev->next = &n;
 | 
|---|
| [8df19af] | 192 |         
 | 
|---|
 | 193 |         #if defined(__ARM_ARCH)
 | 
|---|
| [2ad5e1d5] | 194 |         __asm__ __volatile__ ( "DMB ISH" ::: );
 | 
|---|
| [8df19af] | 195 |         #endif
 | 
|---|
 | 196 | 
 | 
|---|
| [5ece8ce] | 197 |         while( __atomic_load_n(&n.locked, __ATOMIC_RELAXED) ) Pause();
 | 
|---|
| [8df19af] | 198 | 
 | 
|---|
 | 199 |         #if defined(__ARM_ARCH)
 | 
|---|
| [2ad5e1d5] | 200 |         __asm__ __volatile__ ( "DMB ISH" ::: );
 | 
|---|
| [8df19af] | 201 |         #endif
 | 
|---|
| [f835806] | 202 | }
 | 
|---|
 | 203 | 
 | 
|---|
 | 204 | static inline void unlock(mcs_spin_lock & l, mcs_spin_node & n) {
 | 
|---|
| [8df19af] | 205 |         #if defined(__ARM_ARCH)
 | 
|---|
| [2ad5e1d5] | 206 |         __asm__ __volatile__ ( "DMB ISH" ::: );
 | 
|---|
| [8df19af] | 207 |         #endif
 | 
|---|
 | 208 | 
 | 
|---|
| [f835806] | 209 |         mcs_spin_node * n_ptr = &n;
 | 
|---|
| [9e3d123] | 210 |         if (__atomic_compare_exchange_n(&l.queue.tail, &n_ptr, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return;
 | 
|---|
| [5ece8ce] | 211 |         while (__atomic_load_n(&n.next, __ATOMIC_RELAXED) == 0p) Pause();
 | 
|---|
| [8df19af] | 212 | 
 | 
|---|
 | 213 |         #if defined(__ARM_ARCH)
 | 
|---|
| [2ad5e1d5] | 214 |         __asm__ __volatile__ ( "DMB ISH" ::: );
 | 
|---|
| [8df19af] | 215 |         #endif
 | 
|---|
 | 216 | 
 | 
|---|
| [9e3d123] | 217 |         n.next->locked = false;
 | 
|---|
| [f835806] | 218 | }
 | 
|---|
 | 219 | 
 | 
|---|
| [b77f0e1] | 220 | //-----------------------------------------------------------------------------
 | 
|---|
 | 221 | // futex_mutex
 | 
|---|
 | 222 | 
 | 
|---|
 | 223 | // - Kernel thd blocking alternative to the spinlock
 | 
|---|
 | 224 | // - No ownership (will deadlock on reacq)
 | 
|---|
| [5a05946] | 225 | // - no reacq on wakeup
 | 
|---|
| [b77f0e1] | 226 | struct futex_mutex {
 | 
|---|
 | 227 |         // lock state any state other than UNLOCKED is locked
 | 
|---|
 | 228 |         // enum LockState { UNLOCKED = 0, UNCONTENDED = 1, CONTENDED = 2 };
 | 
|---|
 | 229 |         
 | 
|---|
 | 230 |         // stores a lock state
 | 
|---|
 | 231 |         int val; 
 | 
|---|
 | 232 | };
 | 
|---|
 | 233 | 
 | 
|---|
 | 234 | // to use for FUTEX_WAKE and FUTEX_WAIT (other futex calls will need more params)
 | 
|---|
| [7d9598d8] | 235 | static inline int futex(int *uaddr, int futex_op, int val) {
 | 
|---|
| [b77f0e1] | 236 |     return syscall(SYS_futex, uaddr, futex_op, val, NULL, NULL, 0);
 | 
|---|
 | 237 | }
 | 
|---|
 | 238 | 
 | 
|---|
| [5a05946] | 239 | static inline void ?{}( futex_mutex & this ) with(this) { val = 0; }
 | 
|---|
| [b77f0e1] | 240 | 
 | 
|---|
| [5a05946] | 241 | static inline bool internal_try_lock( futex_mutex & this, int & compare_val) with(this) {
 | 
|---|
| [b77f0e1] | 242 |         return __atomic_compare_exchange_n((int*)&val, (int*)&compare_val, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
 | 
|---|
 | 243 | }
 | 
|---|
 | 244 | 
 | 
|---|
| [5a05946] | 245 | static inline int internal_exchange( futex_mutex & this ) with(this) {
 | 
|---|
| [b77f0e1] | 246 |         return __atomic_exchange_n((int*)&val, 2, __ATOMIC_ACQUIRE);
 | 
|---|
 | 247 | }
 | 
|---|
 | 248 | 
 | 
|---|
 | 249 | // if this is called recursively IT WILL DEADLOCK!!!!!
 | 
|---|
| [beeff61e] | 250 | static inline void lock( futex_mutex & this ) with(this) {
 | 
|---|
| [b77f0e1] | 251 |         int state;
 | 
|---|
 | 252 | 
 | 
|---|
| [a45e21c] | 253 |         for( int spin = 4; spin < 1024; spin += spin) {
 | 
|---|
 | 254 |                 state = 0;
 | 
|---|
 | 255 |                 // if unlocked, lock and return
 | 
|---|
 | 256 |                 if (internal_try_lock(this, state)) return;
 | 
|---|
 | 257 |                 if (2 == state) break;
 | 
|---|
 | 258 |                 for (int i = 0; i < spin; i++) Pause();
 | 
|---|
 | 259 |         }
 | 
|---|
| [b77f0e1] | 260 |         
 | 
|---|
 | 261 |         // if not in contended state, set to be in contended state
 | 
|---|
 | 262 |         if (state != 2) state = internal_exchange(this);
 | 
|---|
 | 263 | 
 | 
|---|
 | 264 |         // block and spin until we win the lock
 | 
|---|
 | 265 |         while (state != 0) {
 | 
|---|
 | 266 |                 futex((int*)&val, FUTEX_WAIT, 2); // if val is not 2 this returns with EWOULDBLOCK
 | 
|---|
 | 267 |                 state = internal_exchange(this);
 | 
|---|
 | 268 |         }
 | 
|---|
 | 269 | }
 | 
|---|
 | 270 | 
 | 
|---|
 | 271 | static inline void unlock(futex_mutex & this) with(this) {
 | 
|---|
| [a45e21c] | 272 |         // if uncontended do atomic unlock and then return
 | 
|---|
 | 273 |     if (__atomic_exchange_n(&val, 0, __ATOMIC_RELEASE) == 1) return;
 | 
|---|
| [b77f0e1] | 274 |         
 | 
|---|
 | 275 |         // otherwise threads are blocked so we must wake one
 | 
|---|
 | 276 |         futex((int *)&val, FUTEX_WAKE, 1);
 | 
|---|
 | 277 | }
 | 
|---|
 | 278 | 
 | 
|---|
| [5a05946] | 279 | DEFAULT_ON_NOTIFY( futex_mutex )
 | 
|---|
 | 280 | DEFAULT_ON_WAIT( futex_mutex )
 | 
|---|
 | 281 | DEFAULT_ON_WAKEUP_NO_REACQ( futex_mutex )
 | 
|---|
| [b77f0e1] | 282 | 
 | 
|---|
| [a45e21c] | 283 | //-----------------------------------------------------------------------------
 | 
|---|
 | 284 | // go_mutex
 | 
|---|
 | 285 | 
 | 
|---|
 | 286 | // - Kernel thd blocking alternative to the spinlock
 | 
|---|
 | 287 | // - No ownership (will deadlock on reacq)
 | 
|---|
 | 288 | // - Golang's flavour of mutex
 | 
|---|
 | 289 | // - Impl taken from Golang: src/runtime/lock_futex.go
 | 
|---|
 | 290 | struct go_mutex {
 | 
|---|
 | 291 |         // lock state any state other than UNLOCKED is locked
 | 
|---|
 | 292 |         // enum LockState { UNLOCKED = 0, LOCKED = 1, SLEEPING = 2 };
 | 
|---|
 | 293 |         
 | 
|---|
 | 294 |         // stores a lock state
 | 
|---|
 | 295 |         int val; 
 | 
|---|
 | 296 | };
 | 
|---|
 | 297 | static inline void  ?{}( go_mutex & this ) with(this) { val = 0; }
 | 
|---|
| [7a2c6b18] | 298 | static inline void ?{}( go_mutex & this, go_mutex this2 ) = void;
 | 
|---|
 | 299 | static inline void ?=?( go_mutex & this, go_mutex this2 ) = void;
 | 
|---|
| [a45e21c] | 300 | 
 | 
|---|
 | 301 | static inline bool internal_try_lock(go_mutex & this, int & compare_val, int new_val ) with(this) {
 | 
|---|
 | 302 |         return __atomic_compare_exchange_n((int*)&val, (int*)&compare_val, new_val, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
 | 
|---|
 | 303 | }
 | 
|---|
 | 304 | 
 | 
|---|
 | 305 | static inline int internal_exchange(go_mutex & this, int swap ) with(this) {
 | 
|---|
 | 306 |         return __atomic_exchange_n((int*)&val, swap, __ATOMIC_ACQUIRE);
 | 
|---|
 | 307 | }
 | 
|---|
 | 308 | 
 | 
|---|
 | 309 | // if this is called recursively IT WILL DEADLOCK!!!!!
 | 
|---|
| [beeff61e] | 310 | static inline void lock( go_mutex & this ) with( this ) {
 | 
|---|
| [a45e21c] | 311 |         int state, init_state;
 | 
|---|
 | 312 | 
 | 
|---|
 | 313 |     // speculative grab
 | 
|---|
 | 314 |     state = internal_exchange(this, 1);
 | 
|---|
 | 315 |     if ( !state ) return; // state == 0
 | 
|---|
 | 316 |     init_state = state;
 | 
|---|
 | 317 |     for (;;) {
 | 
|---|
| [bd72c284] | 318 |         for( int i = 0; i < 4; i++ ) {
 | 
|---|
| [a45e21c] | 319 |             while( !val ) { // lock unlocked
 | 
|---|
 | 320 |                 state = 0;
 | 
|---|
| [beeff61e] | 321 |                 if ( internal_try_lock( this, state, init_state ) ) return;
 | 
|---|
| [a45e21c] | 322 |             }
 | 
|---|
| [bd72c284] | 323 |             for (int i = 0; i < 30; i++) Pause();
 | 
|---|
| [a45e21c] | 324 |         }
 | 
|---|
 | 325 | 
 | 
|---|
 | 326 |         while( !val ) { // lock unlocked
 | 
|---|
 | 327 |             state = 0;
 | 
|---|
| [beeff61e] | 328 |             if ( internal_try_lock( this, state, init_state ) ) return;
 | 
|---|
| [a45e21c] | 329 |         }
 | 
|---|
 | 330 |         sched_yield();
 | 
|---|
 | 331 |         
 | 
|---|
 | 332 |         // if not in contended state, set to be in contended state
 | 
|---|
| [beeff61e] | 333 |         state = internal_exchange( this, 2 );
 | 
|---|
| [a45e21c] | 334 |         if ( !state ) return; // state == 0
 | 
|---|
 | 335 |         init_state = 2;
 | 
|---|
| [beeff61e] | 336 |         futex( (int*)&val, FUTEX_WAIT, 2 ); // if val is not 2 this returns with EWOULDBLOCK
 | 
|---|
| [a45e21c] | 337 |     }
 | 
|---|
 | 338 | }
 | 
|---|
 | 339 | 
 | 
|---|
 | 340 | static inline void unlock( go_mutex & this ) with(this) {
 | 
|---|
 | 341 |         // if uncontended do atomic unlock and then return
 | 
|---|
| [beeff61e] | 342 |     if ( __atomic_exchange_n(&val, 0, __ATOMIC_RELEASE) == 1 ) return;
 | 
|---|
| [a45e21c] | 343 |         
 | 
|---|
 | 344 |         // otherwise threads are blocked so we must wake one
 | 
|---|
| [beeff61e] | 345 |         futex( (int *)&val, FUTEX_WAKE, 1 );
 | 
|---|
| [a45e21c] | 346 | }
 | 
|---|
 | 347 | 
 | 
|---|
| [5a05946] | 348 | DEFAULT_ON_NOTIFY( go_mutex )
 | 
|---|
 | 349 | DEFAULT_ON_WAIT( go_mutex )
 | 
|---|
 | 350 | DEFAULT_ON_WAKEUP_NO_REACQ( go_mutex )
 | 
|---|
| [a45e21c] | 351 | 
 | 
|---|
| [7f958c4] | 352 | //-----------------------------------------------------------------------------
 | 
|---|
| [0cee082] | 353 | // Exponential backoff then block lock
 | 
|---|
 | 354 | struct exp_backoff_then_block_lock {
 | 
|---|
| [5a46e09] | 355 |         // Spin lock used for mutual exclusion
 | 
|---|
 | 356 |         __spinlock_t spinlock;
 | 
|---|
 | 357 | 
 | 
|---|
 | 358 |         // List of blocked threads
 | 
|---|
| [e84ab3d] | 359 |         dlist( thread$ ) blocked_threads;
 | 
|---|
| [5a46e09] | 360 | 
 | 
|---|
 | 361 |         // Used for comparing and exchanging
 | 
|---|
 | 362 |         volatile size_t lock_value;
 | 
|---|
 | 363 | };
 | 
|---|
 | 364 | 
 | 
|---|
| [0cee082] | 365 | static inline void  ?{}( exp_backoff_then_block_lock & this ) {
 | 
|---|
| [5a46e09] | 366 |         this.spinlock{};
 | 
|---|
 | 367 |         this.blocked_threads{};
 | 
|---|
 | 368 |         this.lock_value = 0;
 | 
|---|
 | 369 | }
 | 
|---|
| [5a05946] | 370 | static inline void ?{}( exp_backoff_then_block_lock & this, exp_backoff_then_block_lock this2 ) = void;
 | 
|---|
 | 371 | static inline void ?=?( exp_backoff_then_block_lock & this, exp_backoff_then_block_lock this2 ) = void;
 | 
|---|
| [5a46e09] | 372 | 
 | 
|---|
| [a45e21c] | 373 | static inline void  ^?{}( exp_backoff_then_block_lock & this ){}
 | 
|---|
 | 374 | 
 | 
|---|
| [beeff61e] | 375 | static inline bool internal_try_lock( exp_backoff_then_block_lock & this, size_t & compare_val ) with(this) {
 | 
|---|
| [d30e3eb] | 376 |         return __atomic_compare_exchange_n(&lock_value, &compare_val, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
 | 
|---|
| [5a46e09] | 377 | }
 | 
|---|
 | 378 | 
 | 
|---|
| [beeff61e] | 379 | static inline bool try_lock( exp_backoff_then_block_lock & this ) { size_t compare_val = 0; return internal_try_lock( this, compare_val ); }
 | 
|---|
| [5a46e09] | 380 | 
 | 
|---|
| [beeff61e] | 381 | static inline bool try_lock_contention( exp_backoff_then_block_lock & this ) with(this) {
 | 
|---|
 | 382 |         return !__atomic_exchange_n( &lock_value, 2, __ATOMIC_ACQUIRE );
 | 
|---|
| [5a46e09] | 383 | }
 | 
|---|
 | 384 | 
 | 
|---|
| [beeff61e] | 385 | static inline bool block( exp_backoff_then_block_lock & this ) with(this) {
 | 
|---|
| [d30e3eb] | 386 |     lock( spinlock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 387 |     if (__atomic_load_n( &lock_value, __ATOMIC_SEQ_CST) != 2) {
 | 
|---|
 | 388 |         unlock( spinlock );
 | 
|---|
 | 389 |         return true;
 | 
|---|
 | 390 |     }
 | 
|---|
 | 391 |     insert_last( blocked_threads, *active_thread() );
 | 
|---|
 | 392 |     unlock( spinlock );
 | 
|---|
| [5a46e09] | 393 |         park( );
 | 
|---|
 | 394 |         return true;
 | 
|---|
 | 395 | }
 | 
|---|
 | 396 | 
 | 
|---|
| [beeff61e] | 397 | static inline void lock( exp_backoff_then_block_lock & this ) with(this) {
 | 
|---|
| [5a46e09] | 398 |         size_t compare_val = 0;
 | 
|---|
| [b77f0e1] | 399 |         int spin = 4;
 | 
|---|
| [d30e3eb] | 400 | 
 | 
|---|
| [5a46e09] | 401 |         // linear backoff
 | 
|---|
 | 402 |         for( ;; ) {
 | 
|---|
 | 403 |                 compare_val = 0;
 | 
|---|
| [0d4f954] | 404 |                 if (internal_try_lock(this, compare_val)) return;
 | 
|---|
| [5a46e09] | 405 |                 if (2 == compare_val) break;
 | 
|---|
 | 406 |                 for (int i = 0; i < spin; i++) Pause();
 | 
|---|
| [b77f0e1] | 407 |                 if (spin >= 1024) break;
 | 
|---|
| [5a46e09] | 408 |                 spin += spin;
 | 
|---|
 | 409 |         }
 | 
|---|
 | 410 | 
 | 
|---|
| [0d4f954] | 411 |         if(2 != compare_val && try_lock_contention(this)) return;
 | 
|---|
| [b7763da] | 412 |         // block until signalled
 | 
|---|
| [0d4f954] | 413 |         while (block(this)) if(try_lock_contention(this)) return;
 | 
|---|
| [b7763da] | 414 | }
 | 
|---|
 | 415 | 
 | 
|---|
| [beeff61e] | 416 | static inline void unlock( exp_backoff_then_block_lock & this ) with(this) {
 | 
|---|
| [5a46e09] | 417 |     if (__atomic_exchange_n(&lock_value, 0, __ATOMIC_RELEASE) == 1) return;
 | 
|---|
| [d30e3eb] | 418 |     lock( spinlock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 419 |     thread$ * t = &try_pop_front( blocked_threads );
 | 
|---|
 | 420 |     unlock( spinlock );
 | 
|---|
 | 421 |     unpark( t );
 | 
|---|
| [5a46e09] | 422 | }
 | 
|---|
 | 423 | 
 | 
|---|
| [5a05946] | 424 | DEFAULT_ON_NOTIFY( exp_backoff_then_block_lock )
 | 
|---|
 | 425 | DEFAULT_ON_WAIT( exp_backoff_then_block_lock )
 | 
|---|
 | 426 | DEFAULT_ON_WAKEUP_REACQ( exp_backoff_then_block_lock )
 | 
|---|
| [5a46e09] | 427 | 
 | 
|---|
| [7f958c4] | 428 | //-----------------------------------------------------------------------------
 | 
|---|
 | 429 | // Fast Block Lock
 | 
|---|
 | 430 | 
 | 
|---|
| [f835806] | 431 | // minimal blocking lock
 | 
|---|
| [7f958c4] | 432 | // - No reacquire for cond var
 | 
|---|
 | 433 | // - No recursive acquisition
 | 
|---|
 | 434 | // - No ownership
 | 
|---|
 | 435 | struct fast_block_lock {
 | 
|---|
 | 436 |         // List of blocked threads
 | 
|---|
 | 437 |         dlist( thread$ ) blocked_threads;
 | 
|---|
 | 438 | 
 | 
|---|
| [f835806] | 439 |         // Spin lock used for mutual exclusion
 | 
|---|
 | 440 |         __spinlock_t lock;
 | 
|---|
 | 441 | 
 | 
|---|
 | 442 |         // flag showing if lock is held
 | 
|---|
| [7f958c4] | 443 |         bool held:1;
 | 
|---|
 | 444 | };
 | 
|---|
 | 445 | 
 | 
|---|
 | 446 | static inline void  ?{}( fast_block_lock & this ) with(this) {
 | 
|---|
 | 447 |         lock{};
 | 
|---|
 | 448 |         blocked_threads{};
 | 
|---|
 | 449 |         held = false;
 | 
|---|
 | 450 | }
 | 
|---|
 | 451 | static inline void ^?{}( fast_block_lock & this ) {}
 | 
|---|
 | 452 | static inline void ?{}( fast_block_lock & this, fast_block_lock this2 ) = void;
 | 
|---|
 | 453 | static inline void ?=?( fast_block_lock & this, fast_block_lock this2 ) = void;
 | 
|---|
 | 454 | 
 | 
|---|
 | 455 | // if this is called recursively IT WILL DEADLOCK!!!!!
 | 
|---|
| [beeff61e] | 456 | static inline void lock( fast_block_lock & this ) with(this) {
 | 
|---|
| [7f958c4] | 457 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| [b77f0e1] | 458 |         if ( held ) {
 | 
|---|
| [7f958c4] | 459 |                 insert_last( blocked_threads, *active_thread() );
 | 
|---|
 | 460 |                 unlock( lock );
 | 
|---|
 | 461 |                 park( );
 | 
|---|
 | 462 |                 return;
 | 
|---|
 | 463 |         }
 | 
|---|
 | 464 |         held = true;
 | 
|---|
 | 465 |         unlock( lock );
 | 
|---|
 | 466 | }
 | 
|---|
 | 467 | 
 | 
|---|
| [beeff61e] | 468 | static inline void unlock( fast_block_lock & this ) with(this) {
 | 
|---|
| [7f958c4] | 469 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 470 |         /* paranoid */ verifyf( held != false, "Attempt to release lock %p that isn't held", &this );
 | 
|---|
 | 471 |         thread$ * t = &try_pop_front( blocked_threads );
 | 
|---|
 | 472 |         held = ( t ? true : false );
 | 
|---|
 | 473 |         unpark( t );
 | 
|---|
 | 474 |         unlock( lock );
 | 
|---|
 | 475 | }
 | 
|---|
 | 476 | 
 | 
|---|
| [beeff61e] | 477 | static inline void on_notify( fast_block_lock & this, struct thread$ * t ) with(this) {
 | 
|---|
| [0cee082] | 478 |     lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 479 |     insert_last( blocked_threads, *t );
 | 
|---|
 | 480 |     unlock( lock );
 | 
|---|
| [b77f0e1] | 481 | }
 | 
|---|
| [5a05946] | 482 | DEFAULT_ON_WAIT( fast_block_lock )
 | 
|---|
 | 483 | DEFAULT_ON_WAKEUP_NO_REACQ( fast_block_lock )
 | 
|---|
| [7f958c4] | 484 | 
 | 
|---|
| [f835806] | 485 | //-----------------------------------------------------------------------------
 | 
|---|
 | 486 | // simple_owner_lock
 | 
|---|
 | 487 | 
 | 
|---|
 | 488 | // pthread owner lock
 | 
|---|
 | 489 | // - reacquire for cond var
 | 
|---|
 | 490 | // - recursive acquisition
 | 
|---|
 | 491 | // - ownership
 | 
|---|
 | 492 | struct simple_owner_lock {
 | 
|---|
 | 493 |         // List of blocked threads
 | 
|---|
| [beeff61e] | 494 |         dlist( select_node ) blocked_threads;
 | 
|---|
| [f835806] | 495 | 
 | 
|---|
 | 496 |         // Spin lock used for mutual exclusion
 | 
|---|
 | 497 |         __spinlock_t lock;
 | 
|---|
 | 498 | 
 | 
|---|
 | 499 |         // owner showing if lock is held
 | 
|---|
 | 500 |         struct thread$ * owner;
 | 
|---|
 | 501 | 
 | 
|---|
 | 502 |         size_t recursion_count;
 | 
|---|
 | 503 | };
 | 
|---|
 | 504 | 
 | 
|---|
 | 505 | static inline void  ?{}( simple_owner_lock & this ) with(this) {
 | 
|---|
 | 506 |         lock{};
 | 
|---|
 | 507 |         blocked_threads{};
 | 
|---|
 | 508 |         owner = 0p;
 | 
|---|
 | 509 |         recursion_count = 0;
 | 
|---|
 | 510 | }
 | 
|---|
 | 511 | static inline void ^?{}( simple_owner_lock & this ) {}
 | 
|---|
 | 512 | static inline void ?{}( simple_owner_lock & this, simple_owner_lock this2 ) = void;
 | 
|---|
 | 513 | static inline void ?=?( simple_owner_lock & this, simple_owner_lock this2 ) = void;
 | 
|---|
 | 514 | 
 | 
|---|
| [beeff61e] | 515 | static inline void lock( simple_owner_lock & this ) with(this) {
 | 
|---|
 | 516 |         if ( owner == active_thread() ) {
 | 
|---|
| [ae06e0b] | 517 |                 recursion_count++;
 | 
|---|
 | 518 |                 return;
 | 
|---|
 | 519 |         }
 | 
|---|
 | 520 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 521 | 
 | 
|---|
| [beeff61e] | 522 |         if ( owner != 0p ) {
 | 
|---|
 | 523 |         select_node node;
 | 
|---|
 | 524 |                 insert_last( blocked_threads, node );
 | 
|---|
| [ae06e0b] | 525 |                 unlock( lock );
 | 
|---|
 | 526 |                 park( );
 | 
|---|
 | 527 |                 return;
 | 
|---|
 | 528 |         }
 | 
|---|
 | 529 |         owner = active_thread();
 | 
|---|
 | 530 |         recursion_count = 1;
 | 
|---|
 | 531 |         unlock( lock );
 | 
|---|
 | 532 | }
 | 
|---|
 | 533 | 
 | 
|---|
| [beeff61e] | 534 | static inline void pop_node( simple_owner_lock & this ) with(this) {
 | 
|---|
 | 535 |     __handle_waituntil_OR( blocked_threads );
 | 
|---|
 | 536 |     select_node * node = &try_pop_front( blocked_threads );
 | 
|---|
 | 537 |     if ( node ) {
 | 
|---|
 | 538 |         owner = node->blocked_thread;
 | 
|---|
 | 539 |         recursion_count = 1;
 | 
|---|
 | 540 |         // if ( !node->clause_status || __make_select_node_available( *node ) ) unpark( node->blocked_thread );
 | 
|---|
 | 541 |         wake_one( blocked_threads, *node );
 | 
|---|
 | 542 |     } else {
 | 
|---|
 | 543 |         owner = 0p;
 | 
|---|
 | 544 |         recursion_count = 0;
 | 
|---|
 | 545 |     }
 | 
|---|
 | 546 | }
 | 
|---|
| [ae06e0b] | 547 | 
 | 
|---|
| [beeff61e] | 548 | static inline void unlock( simple_owner_lock & this ) with(this) {
 | 
|---|
| [ae06e0b] | 549 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 550 |         /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this );
 | 
|---|
 | 551 |         /* paranoid */ verifyf( owner == active_thread(), "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this );
 | 
|---|
 | 552 |         // if recursion count is zero release lock and set new owner if one is waiting
 | 
|---|
 | 553 |         recursion_count--;
 | 
|---|
 | 554 |         if ( recursion_count == 0 ) {
 | 
|---|
| [beeff61e] | 555 |                 pop_node( this );
 | 
|---|
| [ae06e0b] | 556 |         }
 | 
|---|
 | 557 |         unlock( lock );
 | 
|---|
 | 558 | }
 | 
|---|
 | 559 | 
 | 
|---|
| [fece3d9] | 560 | static inline void on_notify( simple_owner_lock & this, thread$ * t ) with(this) {
 | 
|---|
| [ae06e0b] | 561 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 562 |         // lock held
 | 
|---|
 | 563 |         if ( owner != 0p ) {
 | 
|---|
| [beeff61e] | 564 |                 insert_last( blocked_threads, *(select_node *)t->link_node );
 | 
|---|
| [ae06e0b] | 565 |         }
 | 
|---|
 | 566 |         // lock not held
 | 
|---|
 | 567 |         else {
 | 
|---|
 | 568 |                 owner = t;
 | 
|---|
 | 569 |                 recursion_count = 1;
 | 
|---|
 | 570 |                 unpark( t );
 | 
|---|
 | 571 |         }
 | 
|---|
| [b77f0e1] | 572 |         unlock( lock );
 | 
|---|
| [ae06e0b] | 573 | }
 | 
|---|
 | 574 | 
 | 
|---|
| [fece3d9] | 575 | static inline size_t on_wait( simple_owner_lock & this, __cfa_pre_park pp_fn, void * pp_datum ) with(this) {
 | 
|---|
| [ae06e0b] | 576 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 577 |         /* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this );
 | 
|---|
 | 578 |         /* paranoid */ verifyf( owner == active_thread(), "Thread %p other than the owner %p attempted to release owner lock %p", owner, active_thread(), &this );
 | 
|---|
 | 579 | 
 | 
|---|
 | 580 |         size_t ret = recursion_count;
 | 
|---|
 | 581 | 
 | 
|---|
| [beeff61e] | 582 |         pop_node( this );
 | 
|---|
| [ae06e0b] | 583 | 
 | 
|---|
| [beeff61e] | 584 |     select_node node;
 | 
|---|
 | 585 |     active_thread()->link_node = (void *)&node;
 | 
|---|
| [ae06e0b] | 586 |         unlock( lock );
 | 
|---|
| [fece3d9] | 587 | 
 | 
|---|
 | 588 |     pre_park_then_park( pp_fn, pp_datum );
 | 
|---|
| [beeff61e] | 589 | 
 | 
|---|
| [ae06e0b] | 590 |         return ret;
 | 
|---|
 | 591 | }
 | 
|---|
 | 592 | 
 | 
|---|
| [beeff61e] | 593 | static inline void on_wakeup( simple_owner_lock & this, size_t recursion ) with(this) { recursion_count = recursion; }
 | 
|---|
 | 594 | 
 | 
|---|
 | 595 | // waituntil() support
 | 
|---|
 | 596 | static inline bool register_select( simple_owner_lock & this, select_node & node ) with(this) {
 | 
|---|
 | 597 |     lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 598 | 
 | 
|---|
 | 599 |     // check if we can complete operation. If so race to establish winner in special OR case
 | 
|---|
 | 600 |     if ( !node.park_counter && ( owner == active_thread() || owner == 0p ) ) {
 | 
|---|
 | 601 |         if ( !__make_select_node_available( node ) ) { // we didn't win the race so give up on registering
 | 
|---|
 | 602 |            unlock( lock );
 | 
|---|
 | 603 |            return false;
 | 
|---|
 | 604 |         }
 | 
|---|
 | 605 |     }
 | 
|---|
 | 606 | 
 | 
|---|
 | 607 |     if ( owner == active_thread() ) {
 | 
|---|
 | 608 |                 recursion_count++;
 | 
|---|
 | 609 |         if ( node.park_counter ) __make_select_node_available( node );
 | 
|---|
 | 610 |         unlock( lock );
 | 
|---|
 | 611 |                 return true;
 | 
|---|
 | 612 |         }
 | 
|---|
 | 613 | 
 | 
|---|
 | 614 |     if ( owner != 0p ) {
 | 
|---|
 | 615 |                 insert_last( blocked_threads, node );
 | 
|---|
 | 616 |                 unlock( lock );
 | 
|---|
 | 617 |                 return false;
 | 
|---|
 | 618 |         }
 | 
|---|
 | 619 |     
 | 
|---|
 | 620 |         owner = active_thread();
 | 
|---|
 | 621 |         recursion_count = 1;
 | 
|---|
 | 622 | 
 | 
|---|
 | 623 |     if ( node.park_counter ) __make_select_node_available( node );
 | 
|---|
 | 624 |     unlock( lock );
 | 
|---|
 | 625 |     return true;
 | 
|---|
 | 626 | }
 | 
|---|
 | 627 | 
 | 
|---|
 | 628 | static inline bool unregister_select( simple_owner_lock & this, select_node & node ) with(this) {
 | 
|---|
 | 629 |     lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 630 |     if ( node`isListed ) {
 | 
|---|
 | 631 |         remove( node );
 | 
|---|
 | 632 |         unlock( lock );
 | 
|---|
 | 633 |         return false;
 | 
|---|
 | 634 |     }
 | 
|---|
 | 635 | 
 | 
|---|
 | 636 |     if ( owner == active_thread() ) {
 | 
|---|
 | 637 |         recursion_count--;
 | 
|---|
 | 638 |         if ( recursion_count == 0 ) {
 | 
|---|
 | 639 |             pop_node( this );
 | 
|---|
 | 640 |         }
 | 
|---|
 | 641 |     }
 | 
|---|
 | 642 |     unlock( lock );
 | 
|---|
 | 643 |     return false;
 | 
|---|
 | 644 | }
 | 
|---|
 | 645 | 
 | 
|---|
| [b93bf85] | 646 | static inline bool on_selected( simple_owner_lock & this, select_node & node ) { return true; }
 | 
|---|
| [bf55f32] | 647 | __CFA_SELECT_GET_TYPE( simple_owner_lock );
 | 
|---|
| [ae06e0b] | 648 | 
 | 
|---|
| [f835806] | 649 | //-----------------------------------------------------------------------------
 | 
|---|
 | 650 | // Spin Queue Lock
 | 
|---|
 | 651 | 
 | 
|---|
 | 652 | // - No reacquire for cond var
 | 
|---|
 | 653 | // - No recursive acquisition
 | 
|---|
 | 654 | // - No ownership
 | 
|---|
 | 655 | // - spin lock with no locking/atomics in unlock
 | 
|---|
 | 656 | struct spin_queue_lock {
 | 
|---|
 | 657 |         // Spin lock used for mutual exclusion
 | 
|---|
 | 658 |         mcs_spin_lock lock;
 | 
|---|
 | 659 | 
 | 
|---|
 | 660 |         // flag showing if lock is held
 | 
|---|
| [db7a3ad] | 661 |         volatile bool held;
 | 
|---|
| [f835806] | 662 | };
 | 
|---|
 | 663 | 
 | 
|---|
 | 664 | static inline void  ?{}( spin_queue_lock & this ) with(this) {
 | 
|---|
 | 665 |         lock{};
 | 
|---|
 | 666 |         held = false;
 | 
|---|
 | 667 | }
 | 
|---|
 | 668 | static inline void ^?{}( spin_queue_lock & this ) {}
 | 
|---|
 | 669 | static inline void ?{}( spin_queue_lock & this, spin_queue_lock this2 ) = void;
 | 
|---|
 | 670 | static inline void ?=?( spin_queue_lock & this, spin_queue_lock this2 ) = void;
 | 
|---|
 | 671 | 
 | 
|---|
| [378de69] | 672 | // if this is called recursively IT WILL DEADLOCK!
 | 
|---|
| [beeff61e] | 673 | static inline void lock( spin_queue_lock & this ) with(this) {
 | 
|---|
| [f835806] | 674 |         mcs_spin_node node;
 | 
|---|
 | 675 |         lock( lock, node );
 | 
|---|
| [df932552] | 676 |         while(__atomic_load_n(&held, __ATOMIC_SEQ_CST)) Pause();
 | 
|---|
 | 677 |         __atomic_store_n(&held, true, __ATOMIC_SEQ_CST);
 | 
|---|
| [f835806] | 678 |         unlock( lock, node );
 | 
|---|
 | 679 | }
 | 
|---|
 | 680 | 
 | 
|---|
| [beeff61e] | 681 | static inline void unlock( spin_queue_lock & this ) with(this) {
 | 
|---|
| [2ed32fa7] | 682 |         __atomic_store_n(&held, false, __ATOMIC_RELEASE);
 | 
|---|
| [f835806] | 683 | }
 | 
|---|
 | 684 | 
 | 
|---|
| [5a05946] | 685 | DEFAULT_ON_NOTIFY( spin_queue_lock )
 | 
|---|
 | 686 | DEFAULT_ON_WAIT( spin_queue_lock )
 | 
|---|
 | 687 | DEFAULT_ON_WAKEUP_REACQ( spin_queue_lock )
 | 
|---|
| [f835806] | 688 | 
 | 
|---|
 | 689 | //-----------------------------------------------------------------------------
 | 
|---|
 | 690 | // MCS Block Spin Lock
 | 
|---|
 | 691 | 
 | 
|---|
 | 692 | // - No reacquire for cond var
 | 
|---|
 | 693 | // - No recursive acquisition
 | 
|---|
 | 694 | // - No ownership
 | 
|---|
 | 695 | // - Blocks but first node spins (like spin queue but blocking for not first thd)
 | 
|---|
 | 696 | struct mcs_block_spin_lock {
 | 
|---|
 | 697 |         // Spin lock used for mutual exclusion
 | 
|---|
 | 698 |         mcs_lock lock;
 | 
|---|
 | 699 | 
 | 
|---|
 | 700 |         // flag showing if lock is held
 | 
|---|
| [db7a3ad] | 701 |         volatile bool held;
 | 
|---|
| [f835806] | 702 | };
 | 
|---|
 | 703 | 
 | 
|---|
 | 704 | static inline void  ?{}( mcs_block_spin_lock & this ) with(this) {
 | 
|---|
 | 705 |         lock{};
 | 
|---|
 | 706 |         held = false;
 | 
|---|
 | 707 | }
 | 
|---|
 | 708 | static inline void ^?{}( mcs_block_spin_lock & this ) {}
 | 
|---|
 | 709 | static inline void ?{}( mcs_block_spin_lock & this, mcs_block_spin_lock this2 ) = void;
 | 
|---|
 | 710 | static inline void ?=?( mcs_block_spin_lock & this, mcs_block_spin_lock this2 ) = void;
 | 
|---|
 | 711 | 
 | 
|---|
 | 712 | // if this is called recursively IT WILL DEADLOCK!!!!!
 | 
|---|
| [beeff61e] | 713 | static inline void lock( mcs_block_spin_lock & this ) with(this) {
 | 
|---|
| [f835806] | 714 |         mcs_node node;
 | 
|---|
 | 715 |         lock( lock, node );
 | 
|---|
| [fd365da] | 716 |         while(__atomic_load_n(&held, __ATOMIC_SEQ_CST)) Pause();
 | 
|---|
 | 717 |         __atomic_store_n(&held, true, __ATOMIC_SEQ_CST);
 | 
|---|
| [f835806] | 718 |         unlock( lock, node );
 | 
|---|
 | 719 | }
 | 
|---|
 | 720 | 
 | 
|---|
 | 721 | static inline void unlock(mcs_block_spin_lock & this) with(this) {
 | 
|---|
| [fd365da] | 722 |         __atomic_store_n(&held, false, __ATOMIC_SEQ_CST);
 | 
|---|
| [f835806] | 723 | }
 | 
|---|
 | 724 | 
 | 
|---|
| [5a05946] | 725 | DEFAULT_ON_NOTIFY( mcs_block_spin_lock )
 | 
|---|
 | 726 | DEFAULT_ON_WAIT( mcs_block_spin_lock )
 | 
|---|
 | 727 | DEFAULT_ON_WAKEUP_REACQ( mcs_block_spin_lock )
 | 
|---|
| [f835806] | 728 | 
 | 
|---|
 | 729 | //-----------------------------------------------------------------------------
 | 
|---|
 | 730 | // Block Spin Lock
 | 
|---|
 | 731 | 
 | 
|---|
 | 732 | // - No reacquire for cond var
 | 
|---|
 | 733 | // - No recursive acquisition
 | 
|---|
 | 734 | // - No ownership
 | 
|---|
 | 735 | // - Blocks but first node spins (like spin queue but blocking for not first thd)
 | 
|---|
 | 736 | struct block_spin_lock {
 | 
|---|
 | 737 |         // Spin lock used for mutual exclusion
 | 
|---|
 | 738 |         fast_block_lock lock;
 | 
|---|
 | 739 | 
 | 
|---|
 | 740 |         // flag showing if lock is held
 | 
|---|
| [db7a3ad] | 741 |         volatile bool held;
 | 
|---|
| [f835806] | 742 | };
 | 
|---|
 | 743 | 
 | 
|---|
 | 744 | static inline void  ?{}( block_spin_lock & this ) with(this) {
 | 
|---|
 | 745 |         lock{};
 | 
|---|
 | 746 |         held = false;
 | 
|---|
 | 747 | }
 | 
|---|
 | 748 | static inline void ^?{}( block_spin_lock & this ) {}
 | 
|---|
 | 749 | static inline void ?{}( block_spin_lock & this, block_spin_lock this2 ) = void;
 | 
|---|
 | 750 | static inline void ?=?( block_spin_lock & this, block_spin_lock this2 ) = void;
 | 
|---|
 | 751 | 
 | 
|---|
 | 752 | // if this is called recursively IT WILL DEADLOCK!!!!!
 | 
|---|
| [beeff61e] | 753 | static inline void lock( block_spin_lock & this ) with(this) {
 | 
|---|
| [f835806] | 754 |         lock( lock );
 | 
|---|
| [fd365da] | 755 |         while(__atomic_load_n(&held, __ATOMIC_SEQ_CST)) Pause();
 | 
|---|
 | 756 |         __atomic_store_n(&held, true, __ATOMIC_RELEASE);
 | 
|---|
| [f835806] | 757 |         unlock( lock );
 | 
|---|
 | 758 | }
 | 
|---|
 | 759 | 
 | 
|---|
| [beeff61e] | 760 | static inline void unlock( block_spin_lock & this ) with(this) {
 | 
|---|
| [fd365da] | 761 |         __atomic_store_n(&held, false, __ATOMIC_RELEASE);
 | 
|---|
| [f835806] | 762 | }
 | 
|---|
 | 763 | 
 | 
|---|
| [beeff61e] | 764 | static inline void on_notify( block_spin_lock & this, struct thread$ * t ) with(this.lock) {
 | 
|---|
| [b77f0e1] | 765 |         // first we acquire internal fast_block_lock
 | 
|---|
 | 766 |         lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
 | 767 |         if ( held ) { // if internal fast_block_lock is held
 | 
|---|
 | 768 |                 insert_last( blocked_threads, *t );
 | 
|---|
 | 769 |                 unlock( lock );
 | 
|---|
 | 770 |                 return;
 | 
|---|
 | 771 |         }
 | 
|---|
 | 772 |         // if internal fast_block_lock is not held
 | 
|---|
 | 773 |         held = true;
 | 
|---|
 | 774 |         unlock( lock );
 | 
|---|
 | 775 | 
 | 
|---|
 | 776 |         unpark(t);
 | 
|---|
 | 777 | }
 | 
|---|
| [5a05946] | 778 | DEFAULT_ON_WAIT( block_spin_lock )
 | 
|---|
| [beeff61e] | 779 | static inline void on_wakeup( block_spin_lock & this, size_t recursion ) with(this) {
 | 
|---|
| [b77f0e1] | 780 |         // now we acquire the entire block_spin_lock upon waking up
 | 
|---|
 | 781 |         while(__atomic_load_n(&held, __ATOMIC_SEQ_CST)) Pause();
 | 
|---|
 | 782 |         __atomic_store_n(&held, true, __ATOMIC_RELEASE);
 | 
|---|
 | 783 |         unlock( lock ); // Now we release the internal fast_spin_lock
 | 
|---|
 | 784 | }
 | 
|---|
| [f835806] | 785 | 
 | 
|---|
| [ac5816d] | 786 | //-----------------------------------------------------------------------------
 | 
|---|
| [82f4063] | 787 | // // info_thread
 | 
|---|
 | 788 | // // the info thread is a wrapper around a thread used
 | 
|---|
 | 789 | // // to store extra data for use in the condition variable
 | 
|---|
| [fd54fef] | 790 | forall(L & | is_blocking_lock(L)) {
 | 
|---|
| [ac5816d] | 791 |         struct info_thread;
 | 
|---|
| [848439f] | 792 | }
 | 
|---|
 | 793 | 
 | 
|---|
| [ac5816d] | 794 | //-----------------------------------------------------------------------------
 | 
|---|
 | 795 | // Synchronization Locks
 | 
|---|
| [fd54fef] | 796 | forall(L & | is_blocking_lock(L)) {
 | 
|---|
| [7f958c4] | 797 | 
 | 
|---|
 | 798 |         //-----------------------------------------------------------------------------
 | 
|---|
 | 799 |         // condition_variable
 | 
|---|
 | 800 | 
 | 
|---|
 | 801 |         // The multi-tool condition variable
 | 
|---|
 | 802 |         // - can pass timeouts to wait for either a signal or timeout
 | 
|---|
 | 803 |         // - can wait without passing a lock
 | 
|---|
 | 804 |         // - can have waiters reacquire different locks while waiting on the same cond var
 | 
|---|
 | 805 |         // - has shadow queue
 | 
|---|
 | 806 |         // - can be signalled outside of critical sections with no locks held
 | 
|---|
| [eeb5023] | 807 |         struct condition_variable {
 | 
|---|
| [848439f] | 808 |                 // Spin lock used for mutual exclusion
 | 
|---|
 | 809 |                 __spinlock_t lock;
 | 
|---|
 | 810 | 
 | 
|---|
 | 811 |                 // List of blocked threads
 | 
|---|
| [82f4063] | 812 |                 dlist( info_thread(L) ) blocked_threads;
 | 
|---|
| [848439f] | 813 | 
 | 
|---|
 | 814 |                 // Count of current blocked threads
 | 
|---|
 | 815 |                 int count;
 | 
|---|
 | 816 |         };
 | 
|---|
| [e84ab3d] | 817 | 
 | 
|---|
| [848439f] | 818 | 
 | 
|---|
| [ac5816d] | 819 |         void  ?{}( condition_variable(L) & this );
 | 
|---|
| [848439f] | 820 |         void ^?{}( condition_variable(L) & this );
 | 
|---|
 | 821 | 
 | 
|---|
| [eeb5023] | 822 |         bool notify_one( condition_variable(L) & this );
 | 
|---|
 | 823 |         bool notify_all( condition_variable(L) & this );
 | 
|---|
| [848439f] | 824 | 
 | 
|---|
| [eeb5023] | 825 |         uintptr_t front( condition_variable(L) & this );
 | 
|---|
| [848439f] | 826 | 
 | 
|---|
| [ac5816d] | 827 |         bool empty  ( condition_variable(L) & this );
 | 
|---|
 | 828 |         int  counter( condition_variable(L) & this );
 | 
|---|
| [848439f] | 829 | 
 | 
|---|
| [eeb5023] | 830 |         void wait( condition_variable(L) & this );
 | 
|---|
 | 831 |         void wait( condition_variable(L) & this, uintptr_t info );
 | 
|---|
| [dff1fd1] | 832 |         bool wait( condition_variable(L) & this, Duration duration );
 | 
|---|
 | 833 |         bool wait( condition_variable(L) & this, uintptr_t info, Duration duration );
 | 
|---|
| [848439f] | 834 | 
 | 
|---|
| [eeb5023] | 835 |         void wait( condition_variable(L) & this, L & l );
 | 
|---|
 | 836 |         void wait( condition_variable(L) & this, L & l, uintptr_t info );
 | 
|---|
| [dff1fd1] | 837 |         bool wait( condition_variable(L) & this, L & l, Duration duration );
 | 
|---|
 | 838 |         bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration );
 | 
|---|
| [7f958c4] | 839 | 
 | 
|---|
 | 840 |         //-----------------------------------------------------------------------------
 | 
|---|
 | 841 |         // fast_cond_var
 | 
|---|
 | 842 | 
 | 
|---|
 | 843 |         // The trimmed and slim condition variable
 | 
|---|
 | 844 |         // - no internal lock so you must hold a lock while using this cond var
 | 
|---|
 | 845 |         // - signalling without holding branded lock is UNSAFE!
 | 
|---|
 | 846 |         // - only allows usage of one lock, cond var is branded after usage
 | 
|---|
| [ae06e0b] | 847 | 
 | 
|---|
| [7f958c4] | 848 |         struct fast_cond_var {
 | 
|---|
 | 849 |                 // List of blocked threads
 | 
|---|
 | 850 |                 dlist( info_thread(L) ) blocked_threads;
 | 
|---|
 | 851 |                 #ifdef __CFA_DEBUG__
 | 
|---|
 | 852 |                 L * lock_used;
 | 
|---|
 | 853 |                 #endif
 | 
|---|
 | 854 |         };
 | 
|---|
 | 855 | 
 | 
|---|
 | 856 |         void  ?{}( fast_cond_var(L) & this );
 | 
|---|
 | 857 |         void ^?{}( fast_cond_var(L) & this );
 | 
|---|
 | 858 | 
 | 
|---|
 | 859 |         bool notify_one( fast_cond_var(L) & this );
 | 
|---|
 | 860 |         bool notify_all( fast_cond_var(L) & this );
 | 
|---|
 | 861 | 
 | 
|---|
 | 862 |         uintptr_t front( fast_cond_var(L) & this );
 | 
|---|
 | 863 |         bool empty  ( fast_cond_var(L) & this );
 | 
|---|
 | 864 | 
 | 
|---|
 | 865 |         void wait( fast_cond_var(L) & this, L & l );
 | 
|---|
 | 866 |         void wait( fast_cond_var(L) & this, L & l, uintptr_t info );
 | 
|---|
| [ae06e0b] | 867 | 
 | 
|---|
 | 868 | 
 | 
|---|
 | 869 |         //-----------------------------------------------------------------------------
 | 
|---|
 | 870 |         // pthread_cond_var
 | 
|---|
 | 871 |         //
 | 
|---|
 | 872 |         // - cond var with minimal footprint
 | 
|---|
 | 873 |         // - supports operations needed for phthread cond
 | 
|---|
 | 874 | 
 | 
|---|
 | 875 |         struct pthread_cond_var {
 | 
|---|
 | 876 |                 dlist( info_thread(L) ) blocked_threads;
 | 
|---|
 | 877 |                 __spinlock_t lock;
 | 
|---|
 | 878 |         };
 | 
|---|
 | 879 | 
 | 
|---|
 | 880 |         void  ?{}( pthread_cond_var(L) & this );
 | 
|---|
 | 881 |         void ^?{}( pthread_cond_var(L) & this );
 | 
|---|
 | 882 | 
 | 
|---|
 | 883 |         bool notify_one( pthread_cond_var(L) & this );
 | 
|---|
 | 884 |         bool notify_all( pthread_cond_var(L) & this );
 | 
|---|
 | 885 | 
 | 
|---|
 | 886 |         uintptr_t front( pthread_cond_var(L) & this );
 | 
|---|
 | 887 |         bool empty ( pthread_cond_var(L) & this );
 | 
|---|
 | 888 | 
 | 
|---|
 | 889 |         void wait( pthread_cond_var(L) & this, L & l );
 | 
|---|
 | 890 |         void wait( pthread_cond_var(L) & this, L & l, uintptr_t info );
 | 
|---|
 | 891 |         bool wait( pthread_cond_var(L) & this, L & l, timespec t );
 | 
|---|
 | 892 |         bool wait( pthread_cond_var(L) & this, L & l, uintptr_t info, timespec t );
 | 
|---|
| [8a97248] | 893 | }
 | 
|---|