source: libcfa/src/concurrency/locks.hfa@ 15a0f6f

Last change on this file since 15a0f6f was 34d194c, checked in by Peter A. Buhr <pabuhr@…>, 6 days ago

move libcfa/src/concurrency/atomic.hfa to libcfa/src/bits/atomic.hfa, and update all the atomic operations

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