source: libcfa/src/concurrency/locks.hfa@ c67158d7

Last change on this file since c67158d7 was 34d194c, checked in by Peter A. Buhr <pabuhr@…>, 5 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
Line 
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 : Peter A. Buhr
13// Last Modified On : Sat Jul 4 06:52:06 2026
14// Update Count : 70
15//
16
17#pragma once
18
19#include <stdbool.h>
20#include <stdio.h>
21
22#include "bits/weakso_locks.hfa"
23#include "bits/atomic.hfa"
24#include "collections/lockfree.hfa"
25#include "collections/list.hfa"
26
27#include "limits.hfa"
28#include "thread.hfa"
29
30#include "time_t.hfa"
31#include "time.hfa"
32
33#include "select.hfa"
34
35// futex headers
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
39
40typedef void (*__cfa_pre_park)( void * );
41
42static inline void pre_park_noop( void * ) {}
43
44//-----------------------------------------------------------------------------
45// is_blocking_lock
46forall( L & /*| sized( L )*/ )
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 ) {
59 pp_fn( pp_datum );
60 park();
61}
62
63// macros for default routine impls for is_blocking_lock trait that do not wait-morph
64
65#define DEFAULT_ON_NOTIFY( lock_type ) \
66 static inline void on_notify( lock_type & /*this*/, thread$ * t ){ unpark( t ); }
67
68#define DEFAULT_ON_WAIT( lock_type ) \
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 }
74
75// on_wakeup impl if lock should be reacquired after waking up
76#define DEFAULT_ON_WAKEUP_REACQ( lock_type ) \
77 static inline void on_wakeup( lock_type & this, size_t /*recursion*/ ) { lock( this ); }
78
79// on_wakeup impl if lock will not be reacquired after waking up
80#define DEFAULT_ON_WAKEUP_NO_REACQ( lock_type ) \
81 static inline void on_wakeup( lock_type & /*this*/, size_t /*recursion*/ ) {}
82
83
84//-----------------------------------------------------------------------------
85// Semaphore, counting
86
87struct semaphore {
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
91};
92
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 ); }
101bool V( semaphore & sem );
102size_t V( semaphore & sem, size_t count );
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
116
117//----------
118struct single_acquisition_lock {
119 inline blocking_lock;
120};
121
122static inline void ?{}( single_acquisition_lock & this ) { ((blocking_lock &)this){ false, false }; }
123static inline void ^?{}( single_acquisition_lock & this ) {}
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 ); }
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 ); }
133__CFA_SELECT_GET_TYPE( single_acquisition_lock );
134
135//----------
136struct owner_lock {
137 inline blocking_lock;
138};
139
140static inline void ?{}( owner_lock & this ) { ((blocking_lock &)this){ true, true }; }
141static inline void ^?{}( owner_lock & this ) {}
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 ); }
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 ); }
151__CFA_SELECT_GET_TYPE( owner_lock );
152
153//-----------------------------------------------------------------------------
154// MCS Lock
155struct mcs_node {
156 mcs_node * volatile next;
157 single_sem sem;
158};
159
160static inline void ?{}( mcs_node & this ) { this.next = 0p; }
161
162static inline mcs_node * volatile & next( mcs_node * node ) {
163 return node->next;
164}
165
166struct mcs_lock {
167 mcs_queue( mcs_node ) queue;
168};
169
170static inline void lock( mcs_lock & l, mcs_node & n ) {
171 if ( push( l.queue, &n ) )
172 wait( n.sem );
173}
174
175static inline void unlock( mcs_lock & l, mcs_node & n ) {
176 mcs_node * nxt = advance( l.queue, &n );
177 if ( nxt ) post( nxt->sem );
178}
179
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;
187 volatile bool locked;
188};
189
190struct mcs_spin_queue {
191 mcs_spin_node * volatile tail;
192};
193
194static inline void ?{}( mcs_spin_node & this ) { this.next = 0p; this.locked = true; }
195
196struct mcs_spin_lock {
197 mcs_spin_queue queue;
198};
199
200static inline void lock( mcs_spin_lock & l, mcs_spin_node & n ) {
201 n.locked = true;
202
203 #if defined( __ARM_ARCH )
204 __asm__ __volatile__ ( "DMB ISH" ::: );
205 #endif
206
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;
210
211 #if defined( __ARM_ARCH )
212 __asm__ __volatile__ ( "DMB ISH" ::: );
213 #endif
214
215 while ( __atomic_load_n( &n.locked, __ATOMIC_RELAXED ) ) Pause();
216
217 #if defined( __ARM_ARCH )
218 __asm__ __volatile__ ( "DMB ISH" ::: );
219 #endif
220}
221
222static inline void unlock( mcs_spin_lock & l, mcs_spin_node & n ) {
223 #if defined( __ARM_ARCH )
224 __asm__ __volatile__ ( "DMB ISH" ::: );
225 #endif
226
227 mcs_spin_node * n_ptr = &n;
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();
230
231 #if defined( __ARM_ARCH )
232 __asm__ __volatile__ ( "DMB ISH" ::: );
233 #endif
234
235 n.next->locked = false;
236}
237
238//-----------------------------------------------------------------------------
239// futex_mutex
240
241// - Kernel thd blocking alternative to the spinlock
242// - No ownership (will deadlock on reacq)
243// - no reacq on wakeup
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)
253static inline int futex( int *uaddr, int futex_op, int val ) {
254 return syscall( SYS_futex, uaddr, futex_op, val, NULL, NULL, 0 );
255}
256
257static inline void ?{}( futex_mutex & this ) with( this ) { val = 0; }
258
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 );
261}
262
263static inline int internal_exchange( futex_mutex & this ) with( this ) {
264 return __atomic_exchange_n(( int*)&val, 2, __ATOMIC_ACQUIRE );
265}
266
267// if this is called recursively IT WILL DEADLOCK!!!!!
268static inline void lock( futex_mutex & this ) with( this ) {
269 int state;
270
271 for ( spin; 4 ~ 1024 ~ spin ) {
272 state = 0;
273 // if unlocked, lock and return
274 if ( internal_try_lock( this, state ) ) return;
275 if ( state == 2 ) break;
276 for ( spin ) Pause();
277 }
278
279 // if not in contended state, set to be in contended state
280 if ( state != 2 ) state = internal_exchange( this );
281
282 // block and spin until we win the lock
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 );
286 }
287}
288
289static inline void unlock( futex_mutex & this ) with( this ) {
290 // if uncontended do atomic unlock and then return
291 if ( __atomic_exchange_n( &val, 0, __ATOMIC_RELEASE ) == 1 ) return;
292
293 // otherwise threads are blocked so we must wake one
294 futex(( int *)&val, FUTEX_WAKE, 1 );
295}
296
297DEFAULT_ON_NOTIFY( futex_mutex )
298DEFAULT_ON_WAIT( futex_mutex )
299DEFAULT_ON_WAKEUP_NO_REACQ( futex_mutex )
300
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};
315static inline void ?{}( go_mutex & this ) with( this ) { val = 0; }
316static inline void ?{}( go_mutex & this, go_mutex this2 ) = void;
317static inline void ?=?( go_mutex & this, go_mutex this2 ) = void;
318
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 );
321}
322
323static inline int internal_exchange( go_mutex & this, int swap ) with( this ) {
324 return __atomic_exchange_n( (int*)&val, swap, __ATOMIC_ACQUIRE );
325}
326
327// if this is called recursively IT WILL DEADLOCK!!!!!
328static inline void lock( go_mutex & this ) with( this ) {
329 int state, init_state;
330
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 }
356}
357
358static inline void unlock( go_mutex & this ) with( this ) {
359 // if uncontended do atomic unlock and then return
360 if ( __atomic_exchange_n( &val, 0, __ATOMIC_RELEASE ) == 1 ) return;
361
362 // otherwise threads are blocked so we must wake one
363 futex( (int *)&val, FUTEX_WAKE, 1 );
364}
365
366DEFAULT_ON_NOTIFY( go_mutex )
367DEFAULT_ON_WAIT( go_mutex )
368DEFAULT_ON_WAKEUP_NO_REACQ( go_mutex )
369
370//-----------------------------------------------------------------------------
371// Exponential backoff then block lock
372struct exp_backoff_then_block_lock {
373 // Spin lock used for mutual exclusion
374 __spinlock_t spinlock;
375
376 // List of blocked threads
377 dlist( thread$ ) blocked_threads;
378
379 // Used for comparing and exchanging
380 volatile size_t lock_value;
381};
382
383static inline void ?{}( exp_backoff_then_block_lock & this ) {
384 this.spinlock{};
385 this.blocked_threads{};
386 this.lock_value = 0;
387}
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;
390
391static inline void ^?{}( exp_backoff_then_block_lock & this ){}
392
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 );
395}
396
397static inline bool try_lock( exp_backoff_then_block_lock & this ) { size_t compare_val = 0; return internal_try_lock( this, compare_val ); }
398
399static inline bool try_lock_contention( exp_backoff_then_block_lock & this ) with( this ) {
400 return ! __atomic_exchange_n( &lock_value, 2, __ATOMIC_ACQUIRE );
401}
402
403static inline bool block( exp_backoff_then_block_lock & this ) with( this ) {
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 );
411 park( );
412 return true;
413}
414
415static inline void lock( exp_backoff_then_block_lock & this ) with( this ) {
416 size_t compare_val = 0;
417 int spin = 4;
418
419 // linear backoff
420 for () {
421 compare_val = 0;
422 if ( internal_try_lock( this, compare_val ) ) return;
423 if ( compare_val == 2 ) break;
424 for ( spin ) Pause();
425 if ( spin >= 1024 ) break;
426 spin += spin;
427 }
428
429 if ( 2 != compare_val && try_lock_contention( this ) ) return;
430 // block until signalled
431 while ( block( this ) ) if ( try_lock_contention( this ) ) return;
432}
433
434static inline void unlock( exp_backoff_then_block_lock & this ) with( this ) {
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 );
440}
441
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 )
445
446//-----------------------------------------------------------------------------
447// Fast Block Lock
448
449// minimal blocking lock
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
457 // Spin lock used for mutual exclusion
458 __spinlock_t lock;
459
460 // flag showing if lock is held
461 bool held:1;
462};
463
464static inline void ?{}( fast_block_lock & this ) with( this ) {
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!!!!!
474static inline void lock( fast_block_lock & this ) with( this ) {
475 lock( lock __cfaabi_dbg_ctx2 );
476 if ( held ) {
477 insert_last( blocked_threads, *active_thread() );
478 unlock( lock );
479 park( );
480 return;
481 }
482 held = true;
483 unlock( lock );
484}
485
486static inline void unlock( fast_block_lock & this ) with( this ) {
487 lock( lock __cfaabi_dbg_ctx2 );
488 /* paranoid */ verifyf( held != false, "Attempt to release lock %p that isn't held", &this );
489 thread$ * t = &remove_first( blocked_threads );
490 held = ( t ? true : false );
491 unpark( t );
492 unlock( lock );
493}
494
495static inline void on_notify( fast_block_lock & this, struct thread$ * t ) with( this ) {
496 lock( lock __cfaabi_dbg_ctx2 );
497 insert_last( blocked_threads, *t );
498 unlock( lock );
499}
500DEFAULT_ON_WAIT( fast_block_lock )
501DEFAULT_ON_WAKEUP_NO_REACQ( fast_block_lock )
502
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
512 dlist( select_node ) blocked_threads;
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
523static inline void ?{}( simple_owner_lock & this ) with( this ) {
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
533static inline void lock( simple_owner_lock & this ) with( this ) {
534 if ( owner == active_thread() ) {
535 recursion_count++;
536 return;
537 }
538 lock( lock __cfaabi_dbg_ctx2 );
539
540 if ( owner != 0p ) {
541 select_node node;
542 insert_last( blocked_threads, node );
543 unlock( lock );
544 park( );
545 return;
546 }
547 owner = active_thread();
548 recursion_count = 1;
549 unlock( lock );
550}
551
552static inline void pop_node( simple_owner_lock & this ) with( this ) {
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 }
564}
565
566static inline void unlock( simple_owner_lock & this ) with( this ) {
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 ) {
573 pop_node( this );
574 }
575 unlock( lock );
576}
577
578static inline void on_notify( simple_owner_lock & this, thread$ * t ) with( this ) {
579 lock( lock __cfaabi_dbg_ctx2 );
580 // lock held
581 if ( owner != 0p ) {
582 insert_last( blocked_threads, *(select_node *)t->link_node );
583 }
584 // lock not held
585 else {
586 owner = t;
587 recursion_count = 1;
588 unpark( t );
589 }
590 unlock( lock );
591}
592
593static inline size_t on_wait( simple_owner_lock & this, __cfa_pre_park pp_fn, void * pp_datum ) with( this ) {
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
600 pop_node( this );
601
602 select_node node;
603 active_thread()->link_node = (void *)&node;
604 unlock( lock );
605
606 pre_park_then_park( pp_fn, pp_datum );
607
608 return ret;
609}
610
611static inline void on_wakeup( simple_owner_lock & this, size_t recursion ) with( this ) { recursion_count = recursion; }
612
613// waituntil() support
614static inline bool register_select$( simple_owner_lock & this, select_node & node ) with( this ) {
615 lock( lock __cfaabi_dbg_ctx2 );
616
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 }
624
625 if ( owner == active_thread() ) {
626 recursion_count++;
627 if ( node.park_counter ) __make_select_node_available( node );
628 unlock( lock );
629 return true;
630 }
631
632 if ( owner != 0p ) {
633 insert_last( blocked_threads, node );
634 unlock( lock );
635 return false;
636 }
637
638 owner = active_thread();
639 recursion_count = 1;
640
641 if ( node.park_counter ) __make_select_node_available( node );
642 unlock( lock );
643 return true;
644}
645
646static inline bool unregister_select$( simple_owner_lock & this, select_node & node ) with( this ) {
647 lock( lock __cfaabi_dbg_ctx2 );
648 if ( listed( node ) ) {
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;
662}
663
664static inline bool on_selected$( simple_owner_lock & /*this*/, select_node & /*node*/ ) { return true; }
665__CFA_SELECT_GET_TYPE( simple_owner_lock );
666
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
679 volatile bool held;
680};
681
682static inline void ?{}( spin_queue_lock & this ) with( this ) {
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
690// if this is called recursively IT WILL DEADLOCK!
691static inline void lock( spin_queue_lock & this ) with( this ) {
692 mcs_spin_node node;
693 lock( lock, node );
694 while ( __atomic_load_n( &held, __ATOMIC_SEQ_CST ) ) Pause();
695 __atomic_store_n( &held, true, __ATOMIC_SEQ_CST );
696 unlock( lock, node );
697}
698
699static inline void unlock( spin_queue_lock & this ) with( this ) {
700 __atomic_store_n( &held, false, __ATOMIC_RELEASE );
701}
702
703DEFAULT_ON_NOTIFY( spin_queue_lock )
704DEFAULT_ON_WAIT( spin_queue_lock )
705DEFAULT_ON_WAKEUP_REACQ( spin_queue_lock )
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
719 volatile bool held;
720};
721
722static inline void ?{}( mcs_block_spin_lock & this ) with( this ) {
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!!!!!
731static inline void lock( mcs_block_spin_lock & this ) with( this ) {
732 mcs_node node;
733 lock( lock, node );
734 while ( __atomic_load_n( &held, __ATOMIC_SEQ_CST ) ) Pause();
735 __atomic_store_n( &held, true, __ATOMIC_SEQ_CST );
736 unlock( lock, node );
737}
738
739static inline void unlock( mcs_block_spin_lock & this ) with( this ) {
740 __atomic_store_n( &held, false, __ATOMIC_SEQ_CST );
741}
742
743DEFAULT_ON_NOTIFY( mcs_block_spin_lock )
744DEFAULT_ON_WAIT( mcs_block_spin_lock )
745DEFAULT_ON_WAKEUP_REACQ( mcs_block_spin_lock )
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
759 volatile bool held;
760};
761
762static inline void ?{}( block_spin_lock & this ) with( this ) {
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!!!!!
771static inline void lock( block_spin_lock & this ) with( this ) {
772 lock( lock );
773 while ( __atomic_load_n( &held, __ATOMIC_SEQ_CST ) ) Pause();
774 __atomic_store_n( &held, true, __ATOMIC_RELEASE );
775 unlock( lock );
776}
777
778static inline void unlock( block_spin_lock & this ) with( this ) {
779 __atomic_store_n( &held, false, __ATOMIC_RELEASE );
780}
781
782static inline void on_notify( block_spin_lock & this, struct thread$ * t ) with( this.lock ) {
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
794 unpark( t );
795}
796DEFAULT_ON_WAIT( block_spin_lock )
797 static inline void on_wakeup( block_spin_lock & this, size_t /*recursion*/ ) with( this ) {
798 // now we acquire the entire block_spin_lock upon waking up
799 while ( __atomic_load_n( &held, __ATOMIC_SEQ_CST ) ) Pause();
800 __atomic_store_n( &held, true, __ATOMIC_RELEASE );
801 unlock( lock ); // Now we release the internal fast_spin_lock
802}
803
804//-----------------------------------------------------------------------------
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
808forall( L & | is_blocking_lock( L ) ) {
809 struct info_thread;
810}
811
812//-----------------------------------------------------------------------------
813// Synchronization Locks
814forall( L & | is_blocking_lock( L ) ) {
815
816 //-----------------------------------------------------------------------------
817 // cond_lock
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
825 struct cond_lock {
826 // Spin lock used for mutual exclusion
827 __spinlock_t lock;
828
829 // List of blocked threads
830 dlist( info_thread( L ) ) blocked_threads;
831
832 // Count of current blocked threads
833 int count;
834 };
835
836 void ?{}( cond_lock( L ) & this );
837 void ^?{}( cond_lock( L ) & this );
838
839 bool notify_one( cond_lock( L ) & this );
840 bool notify_all( cond_lock( L ) & this );
841
842 uintptr_t front( cond_lock( L ) & this );
843
844 bool empty ( cond_lock( L ) & this );
845 int counter( cond_lock( L ) & this );
846
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 );
851
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 );
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
864
865 struct fast_cond_var {
866 // List of blocked threads
867 dlist( info_thread( L ) ) blocked_threads;
868 #ifdef __CFA_DEBUG__
869 L * lock_used;
870 #endif
871 };
872
873 void ?{}( fast_cond_var( L ) & this );
874 void ^?{}( fast_cond_var( L ) & this );
875
876 bool notify_one( fast_cond_var( L ) & this );
877 bool notify_all( fast_cond_var( L ) & this );
878
879 uintptr_t front( fast_cond_var( L ) & this );
880 bool empty ( fast_cond_var( L ) & this );
881
882 void wait( fast_cond_var( L ) & this, L & l );
883 void wait( fast_cond_var( L ) & this, L & l, uintptr_t info );
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 {
893 dlist( info_thread( L ) ) blocked_threads;
894 __spinlock_t lock;
895 };
896
897 void ?{}( pthread_cond_var( L ) & this );
898 void ^?{}( pthread_cond_var( L ) & this );
899
900 bool notify_one( pthread_cond_var( L ) & this );
901 bool notify_all( pthread_cond_var( L ) & this );
902
903 uintptr_t front( pthread_cond_var( L ) & this );
904 bool empty ( pthread_cond_var( L ) & this );
905
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 );
910}
Note: See TracBrowser for help on using the repository browser.