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

Last change on this file since baad96e was 2ad5e1d5, checked in by caparson <caparson@…>, 2 years ago

added missing semicolons

  • Property mode set to 100644
File size: 28.1 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
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]39typedef void (*__cfa_pre_park)( void * );
40
41static inline void pre_park_noop( void * ) {}
42
43//-----------------------------------------------------------------------------
44// is_blocking_lock
45forall( L & | sized(L) )
46trait 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
57static 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
86struct semaphore {
87 __spinlock_t lock;
88 int count;
[e84ab3d]89 __queue_t(thread$) waiting;
[f4ec5e45]90};
91
92void ?{}(semaphore & this, int count = 1);
93void ^?{}(semaphore & this);
94bool P (semaphore & this);
95bool V (semaphore & this);
96bool V (semaphore & this, unsigned count);
[e84ab3d]97thread$ * V (semaphore & this, bool );
[f4ec5e45]98
[ab1b971]99//----------
100struct single_acquisition_lock {
101 inline blocking_lock;
102};
103
104static inline void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
105static inline void ^?{}( single_acquisition_lock & this ) {}
[22b7579]106static inline void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); }
107static inline bool try_lock ( single_acquisition_lock & this ) { return try_lock( (blocking_lock &)this ); }
108static inline void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }
[fece3d9]109static 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]110static inline void on_wakeup( single_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
[e84ab3d]111static inline void on_notify( single_acquisition_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); }
[beeff61e]112static inline bool register_select( single_acquisition_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
113static inline bool unregister_select( single_acquisition_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
[b93bf85]114static 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//----------
118struct owner_lock {
119 inline blocking_lock;
120};
121
122static inline void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
123static inline void ^?{}( owner_lock & this ) {}
[f19497c]124static inline void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); }
[d27b6be]125static inline bool try_lock ( owner_lock & this ) { return try_lock( (blocking_lock &)this ); }
[f19497c]126static inline void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); }
[fece3d9]127static 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]128static inline void on_wakeup( owner_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
[e84ab3d]129static inline void on_notify( owner_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); }
[beeff61e]130static inline bool register_select( owner_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
131static inline bool unregister_select( owner_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
[b93bf85]132static 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]137struct mcs_node {
138 mcs_node * volatile next;
139 single_sem sem;
140};
141
[7a2c6b18]142static inline void ?{}( mcs_node & this ) { this.next = 0p; }
[f4ec5e45]143
144static inline mcs_node * volatile & ?`next ( mcs_node * node ) {
145 return node->next;
146}
147
148struct mcs_lock {
149 mcs_queue(mcs_node) queue;
150};
151
[7a2c6b18]152static inline void lock( mcs_lock & l, mcs_node & n ) {
[f4ec5e45]153 if(push(l.queue, &n))
154 wait(n.sem);
155}
156
157static 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
167struct mcs_spin_node {
168 mcs_spin_node * volatile next;
[db7a3ad]169 volatile bool locked;
[f835806]170};
171
172struct mcs_spin_queue {
173 mcs_spin_node * volatile tail;
174};
175
[7a2c6b18]176static inline void ?{}( mcs_spin_node & this ) { this.next = 0p; this.locked = true; }
[f835806]177
178struct mcs_spin_lock {
179 mcs_spin_queue queue;
180};
181
[7a2c6b18]182static 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
204static 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]226struct 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]235static 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]239static inline void ?{}( futex_mutex & this ) with(this) { val = 0; }
[b77f0e1]240
[5a05946]241static 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]245static 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]250static 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
271static 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]279DEFAULT_ON_NOTIFY( futex_mutex )
280DEFAULT_ON_WAIT( futex_mutex )
281DEFAULT_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
290struct 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};
297static inline void ?{}( go_mutex & this ) with(this) { val = 0; }
[7a2c6b18]298static inline void ?{}( go_mutex & this, go_mutex this2 ) = void;
299static inline void ?=?( go_mutex & this, go_mutex this2 ) = void;
[a45e21c]300
301static 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
305static 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]310static 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
340static 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]348DEFAULT_ON_NOTIFY( go_mutex )
349DEFAULT_ON_WAIT( go_mutex )
350DEFAULT_ON_WAKEUP_NO_REACQ( go_mutex )
[a45e21c]351
[7f958c4]352//-----------------------------------------------------------------------------
[0cee082]353// Exponential backoff then block lock
354struct 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]365static inline void ?{}( exp_backoff_then_block_lock & this ) {
[5a46e09]366 this.spinlock{};
367 this.blocked_threads{};
368 this.lock_value = 0;
369}
[5a05946]370static inline void ?{}( exp_backoff_then_block_lock & this, exp_backoff_then_block_lock this2 ) = void;
371static inline void ?=?( exp_backoff_then_block_lock & this, exp_backoff_then_block_lock this2 ) = void;
[5a46e09]372
[a45e21c]373static inline void ^?{}( exp_backoff_then_block_lock & this ){}
374
[beeff61e]375static 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]379static 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]381static 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]385static 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]397static 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]416static 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]424DEFAULT_ON_NOTIFY( exp_backoff_then_block_lock )
425DEFAULT_ON_WAIT( exp_backoff_then_block_lock )
426DEFAULT_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
435struct 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
446static inline void ?{}( fast_block_lock & this ) with(this) {
447 lock{};
448 blocked_threads{};
449 held = false;
450}
451static inline void ^?{}( fast_block_lock & this ) {}
452static inline void ?{}( fast_block_lock & this, fast_block_lock this2 ) = void;
453static inline void ?=?( fast_block_lock & this, fast_block_lock this2 ) = void;
454
455// if this is called recursively IT WILL DEADLOCK!!!!!
[beeff61e]456static 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]468static 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]477static 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]482DEFAULT_ON_WAIT( fast_block_lock )
483DEFAULT_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
492struct 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
505static inline void ?{}( simple_owner_lock & this ) with(this) {
506 lock{};
507 blocked_threads{};
508 owner = 0p;
509 recursion_count = 0;
510}
511static inline void ^?{}( simple_owner_lock & this ) {}
512static inline void ?{}( simple_owner_lock & this, simple_owner_lock this2 ) = void;
513static inline void ?=?( simple_owner_lock & this, simple_owner_lock this2 ) = void;
514
[beeff61e]515static 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]534static 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]548static 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]560static 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]575static 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]593static inline void on_wakeup( simple_owner_lock & this, size_t recursion ) with(this) { recursion_count = recursion; }
594
595// waituntil() support
596static 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
628static 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]646static 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
656struct 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
664static inline void ?{}( spin_queue_lock & this ) with(this) {
665 lock{};
666 held = false;
667}
668static inline void ^?{}( spin_queue_lock & this ) {}
669static inline void ?{}( spin_queue_lock & this, spin_queue_lock this2 ) = void;
670static inline void ?=?( spin_queue_lock & this, spin_queue_lock this2 ) = void;
671
[378de69]672// if this is called recursively IT WILL DEADLOCK!
[beeff61e]673static 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]681static inline void unlock( spin_queue_lock & this ) with(this) {
[2ed32fa7]682 __atomic_store_n(&held, false, __ATOMIC_RELEASE);
[f835806]683}
684
[5a05946]685DEFAULT_ON_NOTIFY( spin_queue_lock )
686DEFAULT_ON_WAIT( spin_queue_lock )
687DEFAULT_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)
696struct 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
704static inline void ?{}( mcs_block_spin_lock & this ) with(this) {
705 lock{};
706 held = false;
707}
708static inline void ^?{}( mcs_block_spin_lock & this ) {}
709static inline void ?{}( mcs_block_spin_lock & this, mcs_block_spin_lock this2 ) = void;
710static inline void ?=?( mcs_block_spin_lock & this, mcs_block_spin_lock this2 ) = void;
711
712// if this is called recursively IT WILL DEADLOCK!!!!!
[beeff61e]713static 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
721static inline void unlock(mcs_block_spin_lock & this) with(this) {
[fd365da]722 __atomic_store_n(&held, false, __ATOMIC_SEQ_CST);
[f835806]723}
724
[5a05946]725DEFAULT_ON_NOTIFY( mcs_block_spin_lock )
726DEFAULT_ON_WAIT( mcs_block_spin_lock )
727DEFAULT_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)
736struct 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
744static inline void ?{}( block_spin_lock & this ) with(this) {
745 lock{};
746 held = false;
747}
748static inline void ^?{}( block_spin_lock & this ) {}
749static inline void ?{}( block_spin_lock & this, block_spin_lock this2 ) = void;
750static inline void ?=?( block_spin_lock & this, block_spin_lock this2 ) = void;
751
752// if this is called recursively IT WILL DEADLOCK!!!!!
[beeff61e]753static 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]760static inline void unlock( block_spin_lock & this ) with(this) {
[fd365da]761 __atomic_store_n(&held, false, __ATOMIC_RELEASE);
[f835806]762}
763
[beeff61e]764static 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]778DEFAULT_ON_WAIT( block_spin_lock )
[beeff61e]779static 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]790forall(L & | is_blocking_lock(L)) {
[ac5816d]791 struct info_thread;
[848439f]792}
793
[ac5816d]794//-----------------------------------------------------------------------------
795// Synchronization Locks
[fd54fef]796forall(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}
Note: See TracBrowser for help on using the repository browser.