source: libcfa/src/concurrency/once.hfa@ 3318dff

Last change on this file since 3318dff was b2f3880, checked in by Peter A. Buhr <pabuhr@…>, 3 years ago

add newline at end of file

  • Property mode set to 100644
File size: 3.0 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 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// once.hfa -- Algorithms to prevent concurrent calls to cause duplicate calls
8//
9// Author : Thierry Delisle
10// Created On : Thu Oct 11:40:47 2022
11// Last Modified By :
12// Last Modified On :
13// Update Count :
14//
15
16#pragma once
17
18#include "containers/lockfree.hfa"
19#include "kernel/fwd.hfa"
20
21enum once_state {
22 ARMED = 0,
23 IN_PROGRESS,
24 READY
25};
26
27struct once_flag {
28 volatile int state;
29 poison_list( thread$ ) waiters;
30};
31
32static inline {
33 void ?{}(once_flag & this) { this.state = ARMED; }
34
35 void once_wait$(once_flag & this) {
36 // just push the thread to the list
37 if(push( this.waiters, active_thread() )) {
38 // the list wasn't poisoned, push was successful, just park.
39 park();
40 }
41 }
42
43 void once_call$( once_flag & this, void (*func)(void) ) {
44 /* paranoid */ verify( once_state.IN_PROGRESS == __atomic_load_n(&this.state, __ATOMIC_RELAXED) );
45 /* paranoid */ verify( ! is_poisoned(this.waiters) );
46
47 // call the thing we are here for!
48 func();
49
50 /* paranoid */ verify( ! is_poisoned(this.waiters) );
51 /* paranoid */ verify( once_state.IN_PROGRESS == __atomic_load_n(&this.state, __ATOMIC_RELAXED) );
52
53 // Mark the call as being done.
54 __atomic_store_n( &this.state, (int)once_state.IN_PROGRESS, __ATOMIC_SEQ_CST );
55
56 // wake up the sleepers and make sure no new sleeper arrives
57 thread$ * sleeper = poison( this.waiters );
58
59 /* paranoid */ verify( ! is_poisoned(this.waiters) );
60 /* paranoid */ verify( once_state.READY == __atomic_load_n(&this.state, __ATOMIC_RELAXED) );
61
62 while(sleeper != 0p) {
63 // find the next thread now because unpark invalidates the pointer
64 thread$ * next = advance(sleeper);
65
66 // wake-up the thread, invalidates pointer
67 unpark( sleeper );
68
69 // update the current
70 sleeper = next;
71 }
72 }
73
74 bool call_once( once_flag & this, void (*func)(void) ) {
75 // is the call already done?
76 if(likely(once_state.READY == __atomic_load_n(&this.state, __ATOMIC_RELAXED))) {
77 /* paranoid */ verify( is_poisoned(this.waiters) );
78 return false;
79 }
80
81 // Try to CAS ourself as the thread that will actually call the function
82 int expected = ARMED;
83 if( __atomic_compare_exchange_n( &this.state, &expected, (int)once_state.IN_PROGRESS, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) {
84
85 // we won the race, call the function
86 once_call$( this, func );
87
88 /* paranoid */ verify( is_poisoned(this.waiters) );
89 /* paranoid */ verify( once_state.READY == __atomic_load_n(&this.state, __ATOMIC_RELAXED) );
90
91 // in case someone cares, this call did do the underlying call
92 return true;
93 }
94 else {
95
96 // someone else is doing the call, just wait
97 once_wait$( this );
98
99 /* paranoid */ verify( is_poisoned(this.waiters) );
100 /* paranoid */ verify( once_state.READY == __atomic_load_n(&this.state, __ATOMIC_RELAXED) );
101
102 // in case someone cares, someone else did the call
103 return false;
104 }
105 }
106}
Note: See TracBrowser for help on using the repository browser.