source: libcfa/src/concurrency/select.hfa@ c4f411e

ADT ast-experimental
Last change on this file since c4f411e was c4f411e, checked in by caparsons <caparson@…>, 2 years ago

fixed a bug which may potentially fix build

  • Property mode set to 100644
File size: 7.1 KB
Line 
1#pragma once
2
3#include "containers/list.hfa"
4// #include "alarm.hfa"
5#include "stdint.h"
6#include "kernel.hfa"
7#include "time.hfa"
8
9struct select_node;
10
11// node status
12static const unsigned long int __SELECT_UNSAT = 0;
13static const unsigned long int __SELECT_PENDING = 1; // used only by special OR case
14static const unsigned long int __SELECT_SAT = 2;
15static const unsigned long int __SELECT_RUN = 3;
16
17
18// these are used inside the compiler to aid in code generation
19static inline bool __CFA_has_clause_run( unsigned long int status ) { return status == __SELECT_RUN; }
20static inline void __CFA_maybe_park( int * park_counter ) {
21 if ( __atomic_sub_fetch( park_counter, 1, __ATOMIC_SEQ_CST) < 0 )
22 park();
23}
24
25// node used for coordinating waituntil synchronization
26struct select_node {
27 int * park_counter; // If this is 0p then the node is in a special OR case waituntil
28 unsigned long int * clause_status; // needs to point at ptr sized location, if this is 0p then node is not part of a waituntil
29
30 void * extra; // used to store arbitrary data needed by some primitives
31
32 thread$ * blocked_thread;
33 inline dlink(select_node);
34};
35P9_EMBEDDED( select_node, dlink(select_node) )
36
37static inline void ?{}( select_node & this ) {
38 this.blocked_thread = active_thread();
39 this.clause_status = 0p;
40 this.park_counter = 0p;
41 this.extra = 0p;
42}
43
44static inline void ?{}( select_node & this, thread$ * blocked_thread ) {
45 this.blocked_thread = blocked_thread;
46 this.clause_status = 0p;
47 this.park_counter = 0p;
48 this.extra = 0p;
49}
50
51static inline void ?{}( select_node & this, thread$ * blocked_thread, void * extra ) {
52 this.blocked_thread = blocked_thread;
53 this.clause_status = 0p;
54 this.park_counter = 0p;
55 this.extra = extra;
56}
57static inline void ^?{}( select_node & this ) {}
58
59// this is used inside the compiler to aid in code generation
60static inline unsigned long int * __get_clause_status( select_node & s ) { return s.clause_status; }
61
62// this is used inside the compiler to attempt to establish an else clause as a winner in the OR special case race
63static inline bool __select_node_else_race( select_node & this ) with( this ) {
64 unsigned long int cmp_status = __SELECT_UNSAT;
65 return *clause_status == 0
66 && __atomic_compare_exchange_n( clause_status, &cmp_status, __SELECT_SAT, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST );
67}
68
69//-----------------------------------------------------------------------------
70// is_selectable
71forall(T & | sized(T))
72trait is_selectable {
73 // For registering a select stmt on a selectable concurrency primitive
74 // Returns bool that indicates if operation is already SAT
75 bool register_select( T &, select_node & );
76
77 // For unregistering a select stmt on a selectable concurrency primitive
78 // If true is returned then the corresponding code block is run (only in non-special OR case and only if node status is not RUN)
79 bool unregister_select( T &, select_node & );
80
81 // This routine is run on the selecting thread prior to executing the statement corresponding to the select_node
82 // passed as an arg to this routine
83 // If on_selected returns false, the statement is not run, if it returns true it is run.
84 bool on_selected( T &, select_node & );
85};
86
87//=============================================================================================
88// Waituntil Helpers
89//=============================================================================================
90
91// used for the 2-stage avail needed by the special OR case
92static inline bool __mark_select_node( select_node & this, unsigned long int val ) with( this ) {
93 /* paranoid */ verify( park_counter == 0p );
94 /* paranoid */ verify( clause_status != 0p );
95
96 unsigned long int cmp_status = __SELECT_UNSAT;
97 while( !__atomic_compare_exchange_n( clause_status, &cmp_status, val, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) {
98 if ( cmp_status != __SELECT_PENDING ) return false;
99 cmp_status = __SELECT_UNSAT;
100 }
101 return true;
102}
103
104static inline void __make_select_node_unsat( select_node & this ) with( this ) {
105 __atomic_store_n( clause_status, __SELECT_UNSAT, __ATOMIC_SEQ_CST );
106}
107
108static inline bool __make_select_node_pending( select_node & this ) with( this ) {
109 return __mark_select_node( this, __SELECT_PENDING );
110}
111
112// when a primitive becomes available it calls the following routine on it's node to update the select state:
113// return true if we want to unpark the thd
114static inline bool __make_select_node_available( select_node & this ) with( this ) {
115 /* paranoid */ verify( clause_status != 0p );
116 if( !park_counter )
117 return __mark_select_node( this, (unsigned long int)&this );
118 // return *clause_status == 0
119 // && __atomic_compare_exchange_n( clause_status, &cmp_status, (unsigned long int)&this, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ); // OR specific case where race was won
120
121 unsigned long int cmp_status = __SELECT_UNSAT;
122
123 return *clause_status == 0 // C_TODO might not need a cmp_xchg in non special OR case
124 && __atomic_compare_exchange_n( clause_status, &cmp_status, __SELECT_SAT, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) // can maybe just use atomic write
125 && !__atomic_add_fetch( park_counter, 1, __ATOMIC_SEQ_CST);
126}
127
128// Handles the special OR case of the waituntil statement
129// Since only one select node can win in the OR case, we need to race to set the node available BEFORE
130// performing the operation since if we lose the race the operation should not be performed as it will be lost
131// Returns true if execution can continue normally and false if the queue has now been drained
132static inline bool __handle_waituntil_OR( dlist( select_node ) & queue ) {
133 if ( queue`isEmpty ) return false;
134 if ( queue`first.clause_status && !queue`first.park_counter ) {
135 while ( !queue`isEmpty ) {
136 // if node not a special OR case or if we win the special OR case race break
137 if ( !queue`first.clause_status || queue`first.park_counter || __make_select_node_available( queue`first ) ) { return true; }
138 // otherwise we lost the special OR race so discard node
139 try_pop_front( queue );
140 }
141 return false;
142 }
143 return true;
144}
145
146// wake one thread from the list
147static inline void wake_one( dlist( select_node ) & queue, select_node & popped ) {
148 if ( !popped.clause_status // normal case, node is not a select node
149 || ( popped.clause_status && !popped.park_counter ) // If popped link is special case OR selecting unpark but don't call __make_select_node_available
150 || __make_select_node_available( popped ) ) // check if popped link belongs to a selecting thread
151 unpark( popped.blocked_thread );
152}
153
154static inline void wake_one( dlist( select_node ) & queue ) { wake_one( queue, try_pop_front( queue ) ); }
155
156static inline void setup_clause( select_node & this, unsigned long int * clause_status, int * park_counter ) {
157 this.blocked_thread = active_thread();
158 this.clause_status = clause_status;
159 this.park_counter = park_counter;
160}
161
162
Note: See TracBrowser for help on using the repository browser.