| [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 | 
 | 
|---|