| [e23b3ce] | 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 | // channel.hfa -- LIBCFATHREAD | 
|---|
|  | 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 2023 | 
|---|
|  | 12 | // Last Modified By : | 
|---|
|  | 13 | // Last Modified On : | 
|---|
|  | 14 | // Update Count     : | 
|---|
|  | 15 | // | 
|---|
|  | 16 |  | 
|---|
| [5e4a830] | 17 | #pragma once | 
|---|
|  | 18 |  | 
|---|
| [55b060d] | 19 | #include "collections/list.hfa" | 
|---|
| [e23b3ce] | 20 | #include "alarm.hfa" | 
|---|
| [beeff61e] | 21 | #include "kernel.hfa" | 
|---|
| [c4f411e] | 22 | #include "time.hfa" | 
|---|
| [339e30a] | 23 |  | 
|---|
| [beeff61e] | 24 | struct select_node; | 
|---|
|  | 25 |  | 
|---|
|  | 26 | // node status | 
|---|
|  | 27 | static const unsigned long int __SELECT_UNSAT = 0; | 
|---|
| [c4f411e] | 28 | static const unsigned long int __SELECT_PENDING = 1; // used only by special OR case | 
|---|
|  | 29 | static const unsigned long int __SELECT_SAT = 2; | 
|---|
|  | 30 | static const unsigned long int __SELECT_RUN = 3; | 
|---|
| [beeff61e] | 31 |  | 
|---|
| [c4f411e] | 32 | // these are used inside the compiler to aid in code generation | 
|---|
| [beeff61e] | 33 | static inline bool __CFA_has_clause_run( unsigned long int status ) { return status == __SELECT_RUN; } | 
|---|
|  | 34 | static inline void __CFA_maybe_park( int * park_counter ) { | 
|---|
|  | 35 | if ( __atomic_sub_fetch( park_counter, 1, __ATOMIC_SEQ_CST) < 0 ) | 
|---|
|  | 36 | park(); | 
|---|
|  | 37 | } | 
|---|
|  | 38 |  | 
|---|
|  | 39 | // node used for coordinating waituntil synchronization | 
|---|
| [339e30a] | 40 | struct select_node { | 
|---|
| [beeff61e] | 41 | int * park_counter;                 // If this is 0p then the node is in a special OR case waituntil | 
|---|
|  | 42 | unsigned long int * clause_status;  // needs to point at ptr sized location, if this is 0p then node is not part of a waituntil | 
|---|
|  | 43 |  | 
|---|
|  | 44 | void * extra;                       // used to store arbitrary data needed by some primitives | 
|---|
|  | 45 |  | 
|---|
| [339e30a] | 46 | thread$ * blocked_thread; | 
|---|
|  | 47 | inline dlink(select_node); | 
|---|
|  | 48 | }; | 
|---|
|  | 49 | P9_EMBEDDED( select_node, dlink(select_node) ) | 
|---|
|  | 50 |  | 
|---|
| [beeff61e] | 51 | static inline void ?{}( select_node & this ) { | 
|---|
|  | 52 | this.blocked_thread = active_thread(); | 
|---|
|  | 53 | this.clause_status = 0p; | 
|---|
|  | 54 | this.park_counter = 0p; | 
|---|
|  | 55 | this.extra = 0p; | 
|---|
| [339e30a] | 56 | } | 
|---|
|  | 57 |  | 
|---|
| [beeff61e] | 58 | static inline void ?{}( select_node & this, thread$ * blocked_thread ) { | 
|---|
| [339e30a] | 59 | this.blocked_thread = blocked_thread; | 
|---|
| [beeff61e] | 60 | this.clause_status = 0p; | 
|---|
|  | 61 | this.park_counter = 0p; | 
|---|
|  | 62 | this.extra = 0p; | 
|---|
| [339e30a] | 63 | } | 
|---|
|  | 64 |  | 
|---|
| [beeff61e] | 65 | static inline void ?{}( select_node & this, thread$ * blocked_thread, void * extra ) { | 
|---|
| [339e30a] | 66 | this.blocked_thread = blocked_thread; | 
|---|
| [beeff61e] | 67 | this.clause_status = 0p; | 
|---|
|  | 68 | this.park_counter = 0p; | 
|---|
|  | 69 | this.extra = extra; | 
|---|
| [339e30a] | 70 | } | 
|---|
| [beeff61e] | 71 | static inline void ^?{}( select_node & this ) {} | 
|---|
| [339e30a] | 72 |  | 
|---|
| [c4f411e] | 73 | // this is used inside the compiler to aid in code generation | 
|---|
| [beeff61e] | 74 | static inline unsigned long int * __get_clause_status( select_node & s ) { return s.clause_status; } | 
|---|
| [339e30a] | 75 |  | 
|---|
| [c4f411e] | 76 | // this is used inside the compiler to attempt to establish an else clause as a winner in the OR special case race | 
|---|
|  | 77 | static inline bool __select_node_else_race( select_node & this ) with( this ) { | 
|---|
|  | 78 | unsigned long int cmp_status = __SELECT_UNSAT; | 
|---|
|  | 79 | return *clause_status == 0 | 
|---|
|  | 80 | && __atomic_compare_exchange_n( clause_status, &cmp_status, __SELECT_SAT, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ); | 
|---|
|  | 81 | } | 
|---|
|  | 82 |  | 
|---|
| [339e30a] | 83 | //----------------------------------------------------------------------------- | 
|---|
|  | 84 | // is_selectable | 
|---|
| [beeff61e] | 85 | forall(T & | sized(T)) | 
|---|
|  | 86 | trait is_selectable { | 
|---|
|  | 87 | // For registering a select stmt on a selectable concurrency primitive | 
|---|
|  | 88 | // Returns bool that indicates if operation is already SAT | 
|---|
|  | 89 | bool register_select( T &, select_node & ); | 
|---|
|  | 90 |  | 
|---|
|  | 91 | // For unregistering a select stmt on a selectable concurrency primitive | 
|---|
|  | 92 | // 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) | 
|---|
| [70a4ed5] | 93 | bool unregister_select( T &, select_node & ); | 
|---|
| [beeff61e] | 94 |  | 
|---|
|  | 95 | // This routine is run on the selecting thread prior to executing the statement corresponding to the select_node | 
|---|
| [bf55f32] | 96 | //    passed as an arg to this routine. If true is returned proceed as normal, if false is returned the statement is skipped | 
|---|
| [b93bf85] | 97 | bool on_selected( T &, select_node & ); | 
|---|
| [339e30a] | 98 | }; | 
|---|
| [bf55f32] | 99 | // Used inside the compiler to allow for overloading on return type for operations such as '?<<?' for channels | 
|---|
|  | 100 | // YOU MUST USE THIS MACRO OR INCLUDE AN EQUIVALENT DECL FOR YOUR TYPE TO SUPPORT WAITUNTIL | 
|---|
|  | 101 | #define __CFA_SELECT_GET_TYPE( typename ) typename __CFA_select_get_type( typename __CFA_t ) | 
|---|
|  | 102 |  | 
|---|
| [339e30a] | 103 |  | 
|---|
| [c4f411e] | 104 | //============================================================================================= | 
|---|
|  | 105 | // Waituntil Helpers | 
|---|
|  | 106 | //============================================================================================= | 
|---|
|  | 107 |  | 
|---|
| [6f774be] | 108 | static inline void __make_select_node_unsat( select_node & this ) with( this ) { | 
|---|
|  | 109 | __atomic_store_n( clause_status, __SELECT_UNSAT, __ATOMIC_SEQ_CST ); | 
|---|
|  | 110 | } | 
|---|
|  | 111 | static inline void __make_select_node_sat( select_node & this ) with( this ) { | 
|---|
|  | 112 | __atomic_store_n( clause_status, __SELECT_SAT, __ATOMIC_SEQ_CST ); | 
|---|
|  | 113 | } | 
|---|
|  | 114 |  | 
|---|
| [c4f411e] | 115 | // used for the 2-stage avail needed by the special OR case | 
|---|
|  | 116 | static inline bool __mark_select_node( select_node & this, unsigned long int val ) with( this ) { | 
|---|
|  | 117 | /* paranoid */ verify( park_counter == 0p ); | 
|---|
|  | 118 | /* paranoid */ verify( clause_status != 0p ); | 
|---|
|  | 119 |  | 
|---|
| [beeff61e] | 120 | unsigned long int cmp_status = __SELECT_UNSAT; | 
|---|
| [c4f411e] | 121 | while( !__atomic_compare_exchange_n( clause_status, &cmp_status, val, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) { | 
|---|
|  | 122 | if ( cmp_status != __SELECT_PENDING ) return false; | 
|---|
|  | 123 | cmp_status = __SELECT_UNSAT; | 
|---|
|  | 124 | } | 
|---|
|  | 125 | return true; | 
|---|
|  | 126 | } | 
|---|
|  | 127 |  | 
|---|
| [6f774be] | 128 | // used for the 2-stage avail by the thread who owns a pending node | 
|---|
|  | 129 | static inline bool __pending_set_other( select_node & other, select_node & mine, unsigned long int val ) with( other ) { | 
|---|
|  | 130 | /* paranoid */ verify( park_counter == 0p ); | 
|---|
|  | 131 | /* paranoid */ verify( clause_status != 0p ); | 
|---|
|  | 132 |  | 
|---|
|  | 133 | unsigned long int cmp_status = __SELECT_UNSAT; | 
|---|
|  | 134 | while( !__atomic_compare_exchange_n( clause_status, &cmp_status, val, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) { | 
|---|
|  | 135 | if ( cmp_status != __SELECT_PENDING ) | 
|---|
|  | 136 | return false; | 
|---|
|  | 137 |  | 
|---|
|  | 138 | // toggle current status flag to avoid starvation/deadlock | 
|---|
|  | 139 | __make_select_node_unsat( mine ); | 
|---|
|  | 140 | cmp_status = __SELECT_UNSAT; | 
|---|
|  | 141 | if ( !__atomic_compare_exchange_n( mine.clause_status, &cmp_status, __SELECT_PENDING, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) | 
|---|
|  | 142 | return false; | 
|---|
|  | 143 | cmp_status = __SELECT_UNSAT; | 
|---|
|  | 144 | } | 
|---|
|  | 145 | return true; | 
|---|
| [e23b3ce] | 146 | } | 
|---|
| [c4f411e] | 147 |  | 
|---|
|  | 148 | static inline bool __make_select_node_pending( select_node & this ) with( this ) { | 
|---|
|  | 149 | return __mark_select_node( this, __SELECT_PENDING ); | 
|---|
| [beeff61e] | 150 | } | 
|---|
|  | 151 |  | 
|---|
|  | 152 | // when a primitive becomes available it calls the following routine on it's node to update the select state: | 
|---|
|  | 153 | // return true if we want to unpark the thd | 
|---|
|  | 154 | static inline bool __make_select_node_available( select_node & this ) with( this ) { | 
|---|
| [c4f411e] | 155 | /* paranoid */ verify( clause_status != 0p ); | 
|---|
|  | 156 | if( !park_counter ) | 
|---|
|  | 157 | return __mark_select_node( this, (unsigned long int)&this ); | 
|---|
| [beeff61e] | 158 |  | 
|---|
| [c4f411e] | 159 | unsigned long int cmp_status = __SELECT_UNSAT; | 
|---|
| [beeff61e] | 160 |  | 
|---|
| [c4f411e] | 161 | return *clause_status == 0 // C_TODO might not need a cmp_xchg in non special OR case | 
|---|
| [beeff61e] | 162 | && __atomic_compare_exchange_n( clause_status, &cmp_status, __SELECT_SAT, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) // can maybe just use atomic write | 
|---|
|  | 163 | && !__atomic_add_fetch( park_counter, 1, __ATOMIC_SEQ_CST); | 
|---|
|  | 164 | } | 
|---|
|  | 165 |  | 
|---|
|  | 166 | // Handles the special OR case of the waituntil statement | 
|---|
|  | 167 | // Since only one select node can win in the OR case, we need to race to set the node available BEFORE | 
|---|
|  | 168 | //    performing the operation since if we lose the race the operation should not be performed as it will be lost | 
|---|
|  | 169 | // Returns true if execution can continue normally and false if the queue has now been drained | 
|---|
|  | 170 | static inline bool __handle_waituntil_OR( dlist( select_node ) & queue ) { | 
|---|
|  | 171 | if ( queue`isEmpty ) return false; | 
|---|
|  | 172 | if ( queue`first.clause_status && !queue`first.park_counter ) { | 
|---|
|  | 173 | while ( !queue`isEmpty ) { | 
|---|
|  | 174 | // if node not a special OR case or if we win the special OR case race break | 
|---|
| [e23b3ce] | 175 | if ( !queue`first.clause_status || queue`first.park_counter || __make_select_node_available( queue`first ) ) | 
|---|
|  | 176 | return true; | 
|---|
| [beeff61e] | 177 | // otherwise we lost the special OR race so discard node | 
|---|
|  | 178 | try_pop_front( queue ); | 
|---|
|  | 179 | } | 
|---|
|  | 180 | return false; | 
|---|
|  | 181 | } | 
|---|
|  | 182 | return true; | 
|---|
|  | 183 | } | 
|---|
|  | 184 |  | 
|---|
|  | 185 | // wake one thread from the list | 
|---|
|  | 186 | static inline void wake_one( dlist( select_node ) & queue, select_node & popped ) { | 
|---|
|  | 187 | if ( !popped.clause_status                              // normal case, node is not a select node | 
|---|
|  | 188 | || ( popped.clause_status && !popped.park_counter ) // If popped link is special case OR selecting unpark but don't call __make_select_node_available | 
|---|
|  | 189 | || __make_select_node_available( popped ) )         // check if popped link belongs to a selecting thread | 
|---|
|  | 190 | unpark( popped.blocked_thread ); | 
|---|
| [339e30a] | 191 | } | 
|---|
| [beeff61e] | 192 |  | 
|---|
|  | 193 | static inline void wake_one( dlist( select_node ) & queue ) { wake_one( queue, try_pop_front( queue ) ); } | 
|---|
|  | 194 |  | 
|---|
|  | 195 | static inline void setup_clause( select_node & this, unsigned long int * clause_status, int * park_counter ) { | 
|---|
|  | 196 | this.blocked_thread = active_thread(); | 
|---|
|  | 197 | this.clause_status = clause_status; | 
|---|
|  | 198 | this.park_counter = park_counter; | 
|---|
|  | 199 | } | 
|---|
|  | 200 |  | 
|---|
| [e23b3ce] | 201 | // waituntil ( timeout( ... ) ) support | 
|---|
|  | 202 | struct select_timeout_node { | 
|---|
|  | 203 | alarm_node_t a_node; | 
|---|
|  | 204 | select_node * s_node; | 
|---|
|  | 205 | }; | 
|---|
|  | 206 | void ?{}( select_timeout_node & this, Duration duration, Alarm_Callback callback ); | 
|---|
|  | 207 | void ^?{}( select_timeout_node & this ); | 
|---|
|  | 208 | void timeout_handler_select_cast( alarm_node_t & node ); | 
|---|
|  | 209 |  | 
|---|
|  | 210 | // Selectable trait routines | 
|---|
|  | 211 | bool register_select( select_timeout_node & this, select_node & node ); | 
|---|
|  | 212 | bool unregister_select( select_timeout_node & this, select_node & node ); | 
|---|
| [b93bf85] | 213 | bool on_selected( select_timeout_node & this, select_node & node ); | 
|---|
| [bf55f32] | 214 | select_timeout_node __CFA_select_get_type( select_timeout_node this ); | 
|---|
| [e23b3ce] | 215 |  | 
|---|
|  | 216 | // Gateway routines to waituntil on duration | 
|---|
|  | 217 | select_timeout_node timeout( Duration duration ); | 
|---|
|  | 218 | select_timeout_node sleep( Duration duration ); | 
|---|
| [bf55f32] | 219 |  | 
|---|