Changeset 5e0b6657 for libcfa/src
- Timestamp:
- Dec 8, 2025, 11:29:33 AM (2 months ago)
- Branches:
- master, stuck-waitfor-destruct
- Children:
- 79ba50c
- Parents:
- 8f448e0 (diff), 79ec8c3 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)links above to see all the changes relative to each parent. - Location:
- libcfa/src
- Files:
-
- 15 edited
-
bits/defs.hfa (modified) (2 diffs)
-
bits/weakso_locks.cfa (modified) (1 diff)
-
bits/weakso_locks.hfa (modified) (2 diffs)
-
concurrency/barrier.hfa (modified) (1 diff)
-
concurrency/channel.hfa (modified) (5 diffs)
-
concurrency/future.hfa (modified) (5 diffs)
-
concurrency/invoke.h (modified) (2 diffs)
-
concurrency/locks.cfa (modified) (4 diffs)
-
concurrency/locks.hfa (modified) (7 diffs)
-
concurrency/monitor.cfa (modified) (3 diffs)
-
concurrency/monitor.hfa (modified) (2 diffs)
-
concurrency/select.cfa (modified) (3 diffs)
-
concurrency/select.hfa (modified) (3 diffs)
-
strstream.cfa (modified) (3 diffs)
-
strstream.hfa (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/bits/defs.hfa
r8f448e0 r5e0b6657 13 13 // Created On : Thu Nov 9 13:24:10 2017 14 14 // Last Modified By : Peter A. Buhr 15 // Last Modified On : Sat Oct 24 10:53:15 202016 // Update Count : 2 115 // Last Modified On : Mon Oct 27 22:40:05 2025 16 // Update Count : 23 17 17 // 18 18 … … 25 25 #define unlikely(x) __builtin_expect(!!(x), 0) 26 26 27 typedef void (* fptr_t)();27 typedef void (* fptr_t)(); 28 28 typedef int_fast16_t __lock_size_t; 29 29 -
libcfa/src/bits/weakso_locks.cfa
r8f448e0 r5e0b6657 28 28 void on_wakeup( blocking_lock &, size_t ) {} 29 29 size_t wait_count( blocking_lock & ) { return 0; } 30 bool register_select ( blocking_lock & this, select_node & node ) { return false; }31 bool unregister_select ( blocking_lock & this, select_node & node ) { return false; }32 bool on_selected ( blocking_lock & this, select_node & node ) { return true; }30 bool register_select$( blocking_lock & this, select_node & node ) { return false; } 31 bool unregister_select$( blocking_lock & this, select_node & node ) { return false; } 32 bool on_selected$( blocking_lock & this, select_node & node ) { return true; } 33 33 -
libcfa/src/bits/weakso_locks.hfa
r8f448e0 r5e0b6657 59 59 void on_wakeup( blocking_lock & this, size_t ) OPTIONAL_THREAD; 60 60 size_t wait_count( blocking_lock & this ) OPTIONAL_THREAD; 61 bool register_select ( blocking_lock & this, select_node & node ) OPTIONAL_THREAD;62 bool unregister_select ( blocking_lock & this, select_node & node ) OPTIONAL_THREAD;63 bool on_selected ( blocking_lock & this, select_node & node ) OPTIONAL_THREAD;61 bool register_select$( blocking_lock & this, select_node & node ) OPTIONAL_THREAD; 62 bool unregister_select$( blocking_lock & this, select_node & node ) OPTIONAL_THREAD; 63 bool on_selected$( blocking_lock & this, select_node & node ) OPTIONAL_THREAD; 64 64 blocking_lock __CFA_select_get_type( blocking_lock this ) OPTIONAL_THREAD; 65 65 … … 78 78 static inline void on_wakeup( multiple_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); } 79 79 static inline void on_notify( multiple_acquisition_lock & this, struct thread$ * t ){ on_notify( (blocking_lock &)this, t ); } 80 static inline bool register_select ( multiple_acquisition_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }81 static inline bool unregister_select ( multiple_acquisition_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }82 static inline bool on_selected ( multiple_acquisition_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }80 static inline bool register_select$( multiple_acquisition_lock & this, select_node & node ) { return register_select$( (blocking_lock &)this, node ); } 81 static inline bool unregister_select$( multiple_acquisition_lock & this, select_node & node ) { return unregister_select$( (blocking_lock &)this, node ); } 82 static inline bool on_selected$( multiple_acquisition_lock & this, select_node & node ) { return on_selected$( (blocking_lock &)this, node ); } 83 83 multiple_acquisition_lock __CFA_select_get_type( multiple_acquisition_lock this ); -
libcfa/src/concurrency/barrier.hfa
r8f448e0 r5e0b6657 11 11 // Created On : Sun Nov 10 08:07:35 2024 12 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Thu Apr 24 22:41:11202514 // Update Count : 1213 // Last Modified On : Fri Oct 31 09:22:14 2025 14 // Update Count : 69 15 15 // 16 16 17 17 #pragma once 18 18 19 #include <locks.hfa> 19 20 #include <monitor.hfa> 20 21 21 22 // Plan 9 inheritance does not work with monitors. Two monitor locks are created. 22 23 24 typedef void (* barrier_fptr_t)( ... ); // like C++, () => void and ... => C () 25 23 26 monitor barrier { 24 unsigned int group, arrivals; // group size, arrival counter 25 condition c; // wait for group to form 27 size_t group$, arrivals$; // group size, arrival counter (backward) 28 condition c$; // wait for group to form 29 barrier_fptr_t callback$; // global callback 30 void * arg$; // global callback argument 26 31 }; 27 32 28 static inline void ?{}( barrier & b, unsigned int group ) { 29 b.group = b.arrivals = group; // arrivals count backward 33 static inline void ?{}( barrier & b, ssize_t group, barrier_fptr_t callback, void * arg ) with ( b ) { 34 [group$, arrivals$] = group; 35 [callback$, arg$] = [callback, arg]; 30 36 } 31 37 32 // Returns a value indicating the reverse order the threads arrived, i.e. last thread returns 0 (and does not block) 33 // hook is an optional hook that is called by the Gth thread before unblocking the other threads. 34 static inline unsigned int block( barrier & mutex b, fptr_t hook = (fptr_t)0 ) with( b ) { 35 arrivals -= 1; // prefix decrement so last is 0 not 1 36 unsigned arrived = b.arrivals; // note arrival order 37 if ( arrivals != 0 ) { // wait for group to form 38 wait( b.c ); 38 static inline void ?{}( barrier & b, ssize_t group ) { (b){ group, 0p, 0p }; } // call base constructor 39 static inline ssize_t waiters( barrier & b ) with( b ) { return group$ - arrivals$; } 40 static inline ssize_t total( barrier & b ) with( b ) { return group$; } 41 42 // Returns a value indicating the reverse order the threads arrived, i.e., the Gth thread returns 0 (and does not 43 // block). olock is an optional mutex lock held by the called and atomically released and block. callback is an 44 // optional function that is called by the Gth thread before unblocking the other threads. arg is an optional (void *) 45 // argument passed to the callback. 46 47 // Barrier is a monitor => implicit mutual exclusion. 48 static inline ssize_t block( barrier & mutex b, owner_lock & olock, barrier_fptr_t callback, void * arg ) with( b ) { 49 arrivals$ -= 1; // prefix decrement so last is 0 not 1 50 typeof( arrivals$ ) arrived = arrivals$; // note arrival order 51 if ( arrivals$ != 0 ) { // wait for group to form 52 if ( &olock != 0p ) unlock( olock ); // if lock specified, release it 53 wait( c$ ); 54 // DO NOT REACQUIRE LOCK TO ALLOW BARGING PREVENTION 39 55 } else { // group formed 40 if ( hook ) hook(); // safe to call 41 signal_all( c ); // unblock group 42 arrivals = group; // reset 56 if ( callback ) callback( arg ); // if callback specified, safe to call with argument 57 else if ( callback$ ) callback$( arg$ ); // if callback specified, safe to call with argument 58 signal_all( c$ ); // unblock group 59 arrivals$ = group$; // reset 43 60 } // if 44 61 return arrived; // return arrival order 45 62 } 63 64 static inline ssize_t block( barrier & b ) { return block( b, *0p, 0p, 0p ); } 65 static inline ssize_t block( barrier & b, owner_lock & olock ) { return block( b, olock, 0p, 0p ); } 66 static inline ssize_t block( barrier & b, barrier_fptr_t callback ) { return block( b, *0p, callback, 0p ); } 67 static inline ssize_t block( barrier & b, barrier_fptr_t callback, void * arg ) { return block( b, *0p, callback, arg ); } 68 static inline ssize_t block( barrier & b, owner_lock & olock, barrier_fptr_t callback ) { return block( b, olock, callback, 0p ); } -
libcfa/src/concurrency/channel.hfa
r8f448e0 r5e0b6657 411 411 } 412 412 413 bool register_select ( chan_read(T) & this, select_node & node ) with(*this.chan, this) {413 bool register_select$( chan_read(T) & this, select_node & node ) with(*this.chan, this) { 414 414 lock( mutex_lock ); 415 415 node.extra = ret; // set .extra so that if it == 0p later in on_selected it is due to channel close … … 476 476 return true; 477 477 } 478 bool unregister_select ( chan_read(T) & this, select_node & node ) { return unregister_chan( *this.chan, node ); }479 bool on_selected ( chan_read(T) & this, select_node & node ) with(this) {478 bool unregister_select$( chan_read(T) & this, select_node & node ) { return unregister_chan( *this.chan, node ); } 479 bool on_selected$( chan_read(T) & this, select_node & node ) with(this) { 480 480 if ( unlikely(node.extra == 0p) ) { 481 481 if ( ! exception_in_flight() ) __closed_remove( *chan, *ret ); // check if woken up due to closed channel … … 491 491 492 492 chan_read_no_ret(T) remove( channel(T) & chan ) { chan_read_no_ret(T) c_read{ chan }; return c_read; } 493 bool register_select ( chan_read_no_ret(T) & this, select_node & node ) {493 bool register_select$( chan_read_no_ret(T) & this, select_node & node ) { 494 494 this.c_read.ret = &this.retval; 495 return register_select ( this.c_read, node );496 } 497 bool unregister_select ( chan_read_no_ret(T) & this, select_node & node ) { return unregister_select( this.c_read, node ); }498 bool on_selected ( chan_read_no_ret(T) & this, select_node & node ) { return on_selected( this.c_read, node ); }495 return register_select$( this.c_read, node ); 496 } 497 bool unregister_select$( chan_read_no_ret(T) & this, select_node & node ) { return unregister_select$( this.c_read, node ); } 498 bool on_selected$( chan_read_no_ret(T) & this, select_node & node ) { return on_selected$( this.c_read, node ); } 499 499 500 500 void ?{}( chan_write(T) & cw, channel(T) * chan, T elem ) { … … 511 511 } 512 512 513 bool register_select ( chan_write(T) & this, select_node & node ) with(*this.chan, this) {513 bool register_select$( chan_write(T) & this, select_node & node ) with(*this.chan, this) { 514 514 lock( mutex_lock ); 515 515 node.extra = &elem; // set .extra so that if it == 0p later in on_selected it is due to channel close … … 577 577 return true; 578 578 } 579 bool unregister_select ( chan_write(T) & this, select_node & node ) { return unregister_chan( *this.chan, node ); }580 581 bool on_selected ( chan_write(T) & this, select_node & node ) with(this) {579 bool unregister_select$( chan_write(T) & this, select_node & node ) { return unregister_chan( *this.chan, node ); } 580 581 bool on_selected$( chan_write(T) & this, select_node & node ) with(this) { 582 582 if ( unlikely(node.extra == 0p) ) { 583 583 if ( ! exception_in_flight() ) __closed_insert( *chan, elem ); // check if woken up due to closed channel -
libcfa/src/concurrency/future.hfa
r8f448e0 r5e0b6657 7 7 // concurrency/future.hfa -- 8 8 // 9 // Author : Thierry Delisle & Peiran Hong & Colby Parsons 9 // Author : Thierry Delisle & Peiran Hong & Colby Parsons & Peter Buhr 10 10 // Created On : Wed Jan 06 17:33:18 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Apr 23 22:41:10202513 // Update Count : 22 12 // Last Modified On : Mon Nov 24 16:08:52 2025 13 // Update Count : 222 14 14 // 15 15 … … 21 21 #include "locks.hfa" 22 22 23 //---------------------------------------------------------------------------- 24 // future 25 // I don't use future_t here as I need to use a lock for this future since it supports multiple consumers. 26 // future_t is lockfree and uses atomics which aren't needed given we use locks here 23 //-------------------------------------------------------------------------------------------------------- 24 // future does not use future_t as it needs a lock to support multiple consumers. future_t is lockfree 25 // and uses atomics which are not needed. 26 //-------------------------------------------------------------------------------------------------------- 27 27 28 forall( T ) { 28 enum { FUTURE_EMPTY = 0, FUTURE_FULFILLED = 1 }; 29 // PRIVATE 30 31 struct future_node$ { 32 inline select_node; 33 T * my_result; 34 }; 35 36 static inline { 37 // memcpy wrapper to help copy values 38 void copy_T$( T & to, T & from ) { memcpy( (void *)&to, (void *)&from, sizeof(T) ); } 39 } // distribution 40 41 enum { FUTURE_EMPTY$ = 0, FUTURE_FULFILLED$ = 1 }; 42 43 // PUBLIC 29 44 30 45 struct future { … … 32 47 T result; 33 48 exception_t * except; 49 futex_mutex lock; 34 50 dlist( select_node ) waiters; 35 futex_mutex lock;36 51 }; 37 __CFA_SELECT_GET_TYPE( future(T) ); 38 39 struct future_node { 40 inline select_node; 41 T * my_result; 42 }; 43 44 static inline { 45 46 void ?{}( future_node(T) & this, thread$ * blocked_thread, T * my_result ) { 47 ((select_node &)this){ blocked_thread }; 48 this.my_result = my_result; 49 } 50 51 void ?{}( future(T) & this ) { 52 this.waiters{}; 53 this.except = 0p; 54 this.state = FUTURE_EMPTY; 55 this.lock{}; 56 } 57 58 void ^?{}( future(T) & this ) { 59 free( this.except ); 60 } 61 62 // Reset future back to original state 63 void reset( future(T) & this ) with(this) { 64 lock( lock ); 65 if ( ! isEmpty( waiters ) ) 66 abort("Attempting to reset a future with blocked waiters"); 67 state = FUTURE_EMPTY; 52 __CFA_SELECT_GET_TYPE( future(T) ); // magic 53 54 static inline { 55 // PRIVATE 56 57 bool register_select$( future(T) & fut, select_node & s ) with( fut ) { // for waituntil statement 58 lock( lock ); 59 60 // check if we can complete operation. If so race to establish winner in special OR case 61 if ( !s.park_counter && state != FUTURE_EMPTY$ ) { 62 if ( !__make_select_node_available( s ) ) { // we didn't win the race so give up on registering 63 unlock( lock ); 64 return false; 65 } 66 } 67 68 // future not ready -> insert select node and return 69 if ( state == FUTURE_EMPTY$ ) { 70 insert_last( waiters, s ); 71 unlock( lock ); 72 return false; 73 } 74 75 __make_select_node_available( s ); 76 unlock( lock ); 77 return true; 78 } 79 80 bool unregister_select$( future(T) & fut, select_node & s ) with( fut ) { // for waituntil statement 81 if ( ! isListed( s ) ) return false; 82 lock( lock ); 83 if ( isListed( s ) ) remove( s ); 84 unlock( lock ); 85 return false; 86 } 87 88 bool on_selected$( future(T) &, select_node & ) { return true; } // for waituntil statement 89 90 // PUBLIC 91 92 // General 93 94 void ?{}( future_node$(T) & fut, thread$ * blocked_thread, T * my_result ) { 95 ((select_node &)fut){ blocked_thread }; 96 fut.my_result = my_result; 97 } 98 99 void ?{}( future(T) & fut ) with( fut ) { 100 except = 0p; 101 state = FUTURE_EMPTY$; 102 } 103 104 void ^?{}( future(T) & fut ) with( fut ) { 68 105 free( except ); 69 except = 0p; 70 unlock( lock ); 71 } 72 73 // check if the future is available 74 // currently no mutual exclusion because I can't see when you need this call to be synchronous or protected 75 bool available( future(T) & this ) { return __atomic_load_n( &this.state, __ATOMIC_RELAXED ); } 76 77 78 // memcpy wrapper to help copy values 79 void copy_T( T & from, T & to ) { 80 memcpy((void *)&to, (void *)&from, sizeof(T)); 81 } 82 83 bool fulfil$( future(T) & this ) with(this) { // helper 84 bool ret_val = ! isEmpty( waiters ); 85 state = FUTURE_FULFILLED; 86 while ( ! isEmpty( waiters ) ) { 87 if ( !__handle_waituntil_OR( waiters ) ) // handle special waituntil OR case 88 break; // if handle_OR returns false then waiters is empty so break 89 select_node &s = remove_first( waiters ); 90 91 if ( s.clause_status == 0p ) // poke in result so that woken threads do not need to reacquire any locks 92 copy_T( result, *(((future_node(T) &)s).my_result) ); 93 94 wake_one( waiters, s ); 95 } 96 unlock( lock ); 97 return ret_val; 98 } 99 100 // Fulfil the future, returns whether or not someone was unblocked 101 bool fulfil( future(T) & this, T val ) with(this) { 102 lock( lock ); 103 if ( state != FUTURE_EMPTY ) 104 abort("Attempting to fulfil a future that has already been fulfilled"); 105 106 copy_T( val, result ); 107 return fulfil$( this ); 108 } 109 110 bool ?()( future(T) & this, T val ) { // alternate interface 111 return fulfil( this, val ); 112 } 113 114 // Load an exception to the future, returns whether or not someone was unblocked 115 bool fulfil( future(T) & this, exception_t * ex ) with(this) { 116 lock( lock ); 117 if ( state != FUTURE_EMPTY ) 118 abort("Attempting to fulfil a future that has already been fulfilled"); 119 120 except = ( exception_t * ) malloc( ex->virtual_table->size ); 121 ex->virtual_table->copy( except, ex ); 122 return fulfil$( this ); 123 } 124 125 bool ?()( future(T) & this, exception_t * ex ) { // alternate interface 126 return fulfil( this, ex ); 127 } 128 129 // Wait for the future to be fulfilled 130 // Also return whether the thread had to block or not 131 [T, bool] get( future(T) & this ) with( this ) { 106 } 107 108 // Used by Client 109 110 // PRIVATE 111 112 // Return a value/exception from the future. 113 T get$( future(T) & fut ) with( fut ) { // helper 132 114 void exceptCheck() { // helper 133 115 if ( except ) { … … 138 120 } 139 121 } 140 141 lock( lock );142 122 T ret_val; 143 if ( state == FUTURE_FULFILLED ) { 123 124 // LOCK ACQUIRED IN PUBLIC get 125 if ( state == FUTURE_FULFILLED$ ) { 144 126 exceptCheck(); 145 copy_T ( result, ret_val);127 copy_T$( ret_val, result ); 146 128 unlock( lock ); 147 return [ret_val, false];148 } 149 150 future_node (T) node = { active_thread(), &ret_val };129 return ret_val; 130 } 131 132 future_node$(T) node = { active_thread(), &ret_val }; 151 133 insert_last( waiters, ((select_node &)node) ); 152 134 unlock( lock ); 153 135 park( ); 154 136 exceptCheck(); 155 156 return [ret_val, true]; 157 } 158 159 // Wait for the future to be fulfilled 160 T get( future(T) & this ) { 161 [T, bool] tt; 162 tt = get(this); 163 return tt.0; 164 } 165 166 T ?()( future(T) & this ) { // alternate interface 167 return get( this ); 168 } 169 170 // Gets value if it is available and returns [ val, true ] 171 // otherwise returns [ default_val, false] 172 // will not block 173 [T, bool] try_get( future(T) & this ) with(this) { 137 return ret_val; 138 } 139 140 // PUBLIC 141 142 bool available( future( T ) & fut ) { return __atomic_load_n( &fut.state, __ATOMIC_RELAXED ); } // future result available ? 143 144 // Return a value/exception from the future. 145 [T, bool] get( future(T) & fut ) with( fut ) { 146 lock( lock ); 147 bool ret = state == FUTURE_EMPTY$; 148 return [ get$( fut ), ret ]; 149 } 150 151 T get( future(T) & fut ) with( fut ) { 152 lock( lock ); 153 return get$( fut ); 154 } 155 T ?()( future(T) & fut ) { return get( fut ); } // alternate interface 156 157 // Non-blocking get: true => return defined value, false => value return undefined. 158 [T, bool] try_get( future(T) & fut ) with( fut ) { 174 159 lock( lock ); 175 160 T ret_val; 176 if ( state == FUTURE_FULFILLED ) {177 copy_T ( result, ret_val);161 if ( state == FUTURE_FULFILLED$ ) { 162 copy_T$( ret_val, result ); 178 163 unlock( lock ); 179 164 return [ret_val, true]; 180 165 } 181 166 unlock( lock ); 182 183 167 return [ret_val, false]; 184 168 } 185 169 186 bool register_select( future(T) & this, select_node & s ) with(this) { 187 lock( lock ); 188 189 // check if we can complete operation. If so race to establish winner in special OR case 190 if ( !s.park_counter && state != FUTURE_EMPTY ) { 191 if ( !__make_select_node_available( s ) ) { // we didn't win the race so give up on registering 192 unlock( lock ); 193 return false; 194 } 195 } 196 197 // future not ready -> insert select node and return 198 if ( state == FUTURE_EMPTY ) { 199 insert_last( waiters, s ); 200 unlock( lock ); 201 return false; 202 } 203 204 __make_select_node_available( s ); 205 unlock( lock ); 206 return true; 207 } 208 209 bool unregister_select( future(T) & this, select_node & s ) with(this) { 210 if ( ! isListed( s ) ) return false; 211 lock( lock ); 212 if ( isListed( s ) ) remove( s ); 213 unlock( lock ); 214 return false; 215 } 216 217 bool on_selected( future(T) &, select_node & ) { return true; } 218 } 219 } 220 221 //-------------------------------------------------------------------------------------------------------- 222 // These futures below do not support select statements so they may not have as many features as 'future' 223 // however the 'single_future' is cheap and cheerful and is most likely more performant than 'future' 224 // since it uses raw atomics and no locks 225 // 226 // As far as 'multi_future' goes I can't see many use cases as it will be less performant than 'future' 227 // since it is monitor based and also is not compatible with select statements 170 // Used by Server 171 172 // PRIVATE 173 174 bool fulfil$( future(T) & fut ) with( fut ) { // helper 175 bool ret_val = ! isEmpty( waiters ); 176 state = FUTURE_FULFILLED$; 177 while ( ! isEmpty( waiters ) ) { 178 if ( !__handle_waituntil_OR( waiters ) ) // handle special waituntil OR case 179 break; // if handle_OR returns false then waiters is empty so break 180 select_node &s = remove_first( waiters ); 181 182 if ( s.clause_status == 0p ) // poke in result so that woken threads do not need to reacquire any locks 183 copy_T$( *(((future_node$(T) &)s).my_result), result ); 184 185 wake_one( waiters, s ); 186 } 187 unlock( lock ); 188 return ret_val; 189 } 190 191 // PUBLIC 192 193 // Load a value/exception into the future, returns whether or not waiting threads. 194 bool fulfil( future(T) & fut, T val ) with( fut ) { 195 lock( lock ); 196 if ( state != FUTURE_EMPTY$ ) abort("Attempting to fulfil a future that has already been fulfilled"); 197 copy_T$( result, val ); 198 return fulfil$( fut ); 199 } 200 bool ?()( future(T) & fut, T val ) { return fulfil( fut, val ); } // alternate interface 201 202 bool fulfil( future(T) & fut, exception_t * ex ) with( fut ) { 203 lock( lock ); 204 if ( state != FUTURE_EMPTY$ ) abort( "Attempting to fulfil a future that has already been fulfilled" ); 205 except = ( exception_t * ) malloc( ex->virtual_table->size ); 206 ex->virtual_table->copy( except, ex ); 207 return fulfil$( fut ); 208 } 209 bool ?()( future(T) & fut, exception_t * ex ) { return fulfil( fut, ex ); } // alternate interface 210 211 void reset( future(T) & fut ) with( fut ) { // mark future as empty (for reuse) 212 lock( lock ); 213 if ( ! isEmpty( waiters ) ) abort( "Attempting to reset a future with blocked waiters" ); 214 state = FUTURE_EMPTY$; 215 free( except ); 216 except = 0p; 217 unlock( lock ); 218 } 219 } // static inline 220 } // forall( T ) 221 222 //-------------------------------------------------------------------------------------------------------- 223 // future_rc uses reference counting to eliminate explicit storage-management and support the waituntil 224 // statement. 228 225 //-------------------------------------------------------------------------------------------------------- 229 226 230 227 forall( T ) { 228 // PRIVATE 229 230 struct future_rc_impl$ { 231 futex_mutex lock; // concurrent protection 232 size_t refCnt; // number of references to future 233 future(T) fut; // underlying future 234 }; // future_rc_impl$ 235 236 static inline { 237 size_t incRef$( future_rc_impl$( T ) & impl ) with( impl ) { 238 return __atomic_fetch_add( &refCnt, 1, __ATOMIC_SEQ_CST ); 239 } // incRef$ 240 241 size_t decRef$( future_rc_impl$( T ) & impl ) with( impl ) { 242 return __atomic_fetch_add( &refCnt, -1, __ATOMIC_SEQ_CST ); 243 } // decRef$ 244 245 void ?{}( future_rc_impl$( T ) & frc ) with( frc ) { 246 refCnt = 1; // count initial object 247 } // ?{} 248 } // static inline 249 250 // PUBLIC 251 252 struct future_rc { 253 future_rc_impl$(T) * impl; 254 }; // future_rc 255 __CFA_SELECT_GET_TYPE( future_rc(T) ); // magic 256 257 static inline { 258 // PRIVATE 259 260 bool register_select$( future_rc(T) & frc, select_node & s ) with( frc ) { // for waituntil statement 261 return register_select$( frc.impl->fut, s ); 262 } 263 264 bool unregister_select$( future_rc(T) & frc, select_node & s ) with( frc ) { // for waituntil statement 265 return unregister_select$( frc.impl->fut, s ); 266 } 267 268 bool on_selected$( future_rc(T) &, select_node & ) { return true; } // for waituntil statement 269 270 // PUBLIC 271 272 // General 273 274 void ?{}( future_rc( T ) & frc ) with( frc ) { // default constructor 275 impl = new(); 276 } // ?{} 277 278 void ?{}( future_rc( T ) & to, future_rc( T ) & from ) with( to ) { // copy constructor 279 impl = from.impl; // point at new impl 280 incRef$( *impl ); 281 } // ?{} 282 283 void ^?{}( future_rc( T ) & frc ) with( frc ) { 284 if ( decRef$( *impl ) == 1 ) { delete( impl ); impl = 0p; } 285 } // ^?{} 286 287 future_rc( T ) & ?=?( future_rc( T ) & lhs, future_rc( T ) & rhs ) with( lhs ) { 288 if ( impl == rhs.impl ) return lhs; // self assignment ? 289 if ( decRef$( *impl ) == 1 ) { delete( impl ); impl = 0p; } // no references ? => delete current impl 290 impl = rhs.impl; // point at new impl 291 incRef$( *impl ); // and increment reference count 292 return lhs; 293 } // ?+? 294 295 // Used by Client 296 297 bool available( future_rc( T ) & frc ) { return available( frc.impl->fut ); } // future result available ? 298 299 // Return a value/exception from the future. 300 [T, bool] get( future_rc(T) & frc ) with( frc ) { return get( impl->fut ); } // return future value 301 T get( future_rc(T) & frc ) with( frc ) { return get( impl->fut ); } // return future value 302 T ?()( future_rc(T) & frc ) with( frc ) { return get( frc ); } // alternate interface 303 [T, bool] try_get( future_rc(T) & frc ) with( frc ) { return try_get( impl->fut ); } 304 305 int ?==?( future_rc( T ) & lhs, future_rc( T ) & rhs ) { return lhs.impl == rhs.impl; } // referential equality 306 307 // Used by Server 308 309 // Load a value/exception into the future, returns whether or not waiting threads. 310 bool fulfil( future_rc(T) & frc, T val ) with( frc ) { return fulfil( impl->fut, val ); } // copy-in future value 311 bool ?()( future_rc(T) & frc, T val ) { return fulfil( frc, val ); } // alternate interface 312 313 bool fulfil( future_rc(T) & frc, exception_t * ex ) with( frc ) { return fulfil( impl->fut, ex ); } // insert future exception 314 bool ?()( future_rc(T) & frc, exception_t * ex ) { return fulfil( frc, ex ); } // alternate interface 315 316 void reset( future_rc(T) & frc ) with( frc ) { reset( impl->fut ); } // mark future as empty (for reuse) 317 } // static inline 318 } // forall( T ) 319 320 //-------------------------------------------------------------------------------------------------------- 321 // This future does not support waituntil statements so it does not have as many features as 'future'. 322 // However, it is cheap and cheerful and is more performant than 'future' since it uses raw atomics 323 // and no locks 324 //-------------------------------------------------------------------------------------------------------- 325 326 forall( T ) { 327 // PUBLIC 328 231 329 struct single_future { 232 330 inline future_t; … … 235 333 236 334 static inline { 237 // Reset future back to original state 238 void reset(single_future(T) & this) { reset( (future_t&)this ); } 239 240 // check if the future is available 241 bool available( single_future(T) & this ) { return available( (future_t&)this ); } 242 243 // Mark the future as abandoned, meaning it will be deleted by the server 244 // This doesn't work beause of the potential need for a destructor 245 // void abandon( single_future(T) & this ); 246 247 // Fulfil the future, returns whether or not someone was unblocked 248 thread$ * fulfil( single_future(T) & this, T result ) { 249 this.result = result; 250 return fulfil( (future_t&)this ); 251 } 252 253 // Wait for the future to be fulfilled 254 // Also return whether the thread had to block or not 255 [T, bool] wait( single_future(T) & this ) { 256 bool r = wait( (future_t&)this ); 257 return [this.result, r]; 258 } 259 260 // Wait for the future to be fulfilled 261 T wait( single_future(T) & this ) { 262 [T, bool] tt; 263 tt = wait(this); 264 return tt.0; 265 } 266 } 267 } 268 269 forall( T ) { 270 monitor multi_future { 271 inline future_t; 272 condition blocked; 273 bool has_first; 274 T result; 275 }; 276 277 static inline { 278 void ?{}(multi_future(T) & this) { 279 this.has_first = false; 280 } 281 282 bool $first( multi_future(T) & mutex this ) { 283 if ( this.has_first ) { 284 wait( this.blocked ); 285 return false; 286 } 287 288 this.has_first = true; 289 return true; 290 } 291 292 void $first_done( multi_future(T) & mutex this ) { 293 this.has_first = false; 294 signal_all( this.blocked ); 295 } 296 297 // Reset future back to original state 298 void reset(multi_future(T) & mutex this) { 299 if ( this.has_first != false ) abort("Attempting to reset a multi_future with at least one blocked threads"); 300 if ( !is_empty(this.blocked) ) abort("Attempting to reset a multi_future with multiple blocked threads"); 301 reset( (future_t&)*(future_t*)((uintptr_t)&this + sizeof(monitor$)) ); 302 } 303 304 // Fulfil the future, returns whether or not someone was unblocked 305 bool fulfil( multi_future(T) & this, T result ) { 306 this.result = result; 307 return fulfil( (future_t&)*(future_t*)((uintptr_t)&this + sizeof(monitor$)) ) != 0p; 308 } 309 310 // Wait for the future to be fulfilled 311 // Also return whether the thread had to block or not 312 [T, bool] wait( multi_future(T) & this ) { 313 bool sw = $first( this ); 314 bool w = !sw; 315 if ( sw ) { 316 w = wait( (future_t&)*(future_t*)((uintptr_t)&this + sizeof(monitor$)) ); 317 $first_done( this ); 318 } 319 320 return [this.result, w]; 321 } 322 323 // Wait for the future to be fulfilled 324 T wait( multi_future(T) & this ) { 325 return wait(this).0; 326 } 327 } 328 } 335 // PUBLIC 336 337 bool available( single_future(T) & fut ) { return available( (future_t &)fut ); } // future result available ? 338 339 // Return a value/exception from the future. 340 [T, bool] get( single_future(T) & fut ) { return [fut.result, wait( fut )]; } 341 T get( single_future(T) & fut ) { wait( fut ); return fut.result; } 342 T ?()( single_future(T) & fut ) { return get( fut ); } // alternate interface 343 344 // Load a value into the future, returns whether or not waiting threads. 345 bool fulfil( single_future(T) & fut, T result ) { 346 fut.result = result; 347 return fulfil( (future_t &)fut ) != 0p; 348 } 349 bool ?()( single_future(T) & fut, T val ) { return fulfil( fut, val ); } // alternate interface 350 351 void reset( single_future(T) & fut ) { reset( (future_t &)fut ); } // mark future as empty (for reuse) 352 } // static inline 353 } // forall( T ) -
libcfa/src/concurrency/invoke.h
r8f448e0 r5e0b6657 10 10 // Created On : Tue Jan 17 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Apr 23 15:27:18202513 // Update Count : 6 112 // Last Modified On : Fri Oct 31 08:46:59 2025 13 // Update Count : 67 14 14 // 15 15 … … 236 236 void * link_node; 237 237 238 // extra fields supporting task related features 239 uintptr_t shadow$; // user information for shadow queue 238 240 PRNG_STATE_T random_state; // fast random numbers 239 241 -
libcfa/src/concurrency/locks.cfa
r8f448e0 r5e0b6657 186 186 187 187 // waituntil() support 188 bool register_select ( blocking_lock & this, select_node & node ) with(this) {188 bool register_select$( blocking_lock & this, select_node & node ) with(this) { 189 189 lock( lock __cfaabi_dbg_ctx2 ); 190 190 thread$ * thrd = active_thread(); … … 218 218 } 219 219 220 bool unregister_select ( blocking_lock & this, select_node & node ) with(this) {220 bool unregister_select$( blocking_lock & this, select_node & node ) with(this) { 221 221 lock( lock __cfaabi_dbg_ctx2 ); 222 222 if ( isListed( node ) ) { … … 239 239 } 240 240 241 bool on_selected ( blocking_lock & this, select_node & node ) { return true; }241 bool on_selected$( blocking_lock & this, select_node & node ) { return true; } 242 242 243 243 //----------------------------------------------------------------------------- … … 583 583 } 584 584 } 585 585 586 //----------------------------------------------------------------------------- 586 // Semaphore 587 void ?{}( semaphore & this, int count = 1 ) { 588 (this.lock){}; 589 this.count = count; 590 (this.waiting){}; 591 } 592 void ^?{}(semaphore & this) {} 593 594 bool P(semaphore & this) with( this ){ 595 lock( lock __cfaabi_dbg_ctx2 ); 596 count -= 1; 597 if ( count < 0 ) { 598 // queue current task 599 append( waiting, active_thread() ); 600 601 // atomically release spin lock and block 602 unlock( lock ); 587 // Semaphore, counting 588 589 void ?{}( semaphore & sem, ssize_t count = 1 ) with( sem ) { 590 count$ = count; 591 (lock$){}; 592 (waiting$){}; 593 } 594 595 bool P( semaphore & sem ) with( sem ) { 596 lock( lock$ __cfaabi_dbg_ctx2 ); 597 count$ -= 1; 598 if ( count$ < 0 ) { // in use ? 599 append( waiting$, active_thread() ); // queue current task 600 unlock( lock$ ); // atomically release spin lock and block 603 601 park(); 604 return true; 602 return false; 603 } // if 604 unlock( lock$ ); 605 return true; 606 } 607 608 bool P( semaphore & sem, semaphore & lock ) with( sem ) { 609 lock( lock$ __cfaabi_dbg_ctx2 ); 610 if ( &sem == &lock ) { // perform operation on self ? 611 if ( count$ < 0 ) { // V my semaphore 612 unpark( pop_head( waiting$ ) ); // unblock task at head of waiting list 613 } // if 605 614 } else { 606 unlock( lock ); 615 V( lock ); // V mutex semaphore 616 count$ -= 1; 617 } // if 618 619 if ( count$ < 0 ) { // in use ? 620 append( waiting$, active_thread() ); // queue current task 621 unlock( lock$ ); // atomically release spin lock and block 622 park(); 607 623 return false; 608 } 609 } 610 611 thread$ * V (semaphore & this, const bool doUnpark ) with( this ) { 624 } // if 625 unlock( lock$ ); 626 return true; 627 } 628 629 bool try_P( semaphore & sem ) with( sem ) { 630 lock( lock$ __cfaabi_dbg_ctx2 ); 631 if ( count$ <= 0 ) { // in use ? 632 unlock( lock$ ); 633 return false; 634 } // if 635 count$ -= 1; // acquire 636 unlock( lock$ ); 637 return true; 638 } 639 640 bool V( semaphore & sem ) with( sem ) { 612 641 thread$ * thrd = 0p; 613 lock( lock __cfaabi_dbg_ctx2 ); 614 count += 1; 615 if ( count <= 0 ) { 616 // remove task at head of waiting list 617 thrd = pop_head( waiting ); 618 } 619 620 unlock( lock ); 621 622 // make new owner 623 if ( doUnpark ) unpark( thrd ); 624 625 return thrd; 626 } 627 628 bool V(semaphore & this) with( this ) { 629 thread$ * thrd = V(this, true); 642 lock( lock$ __cfaabi_dbg_ctx2 ); 643 count$ += 1; 644 if ( count$ <= 0 ) { 645 thrd = pop_head( waiting$ ); // remove task at head of waiting list 646 } 647 unlock( lock$ ); 648 if ( true ) unpark( thrd ); // make new owner 630 649 return thrd != 0p; 631 650 } 632 651 633 bool V(semaphore & this, unsigned diff) with( this ) { 634 thread$ * thrd = 0p; 635 lock( lock __cfaabi_dbg_ctx2 ); 636 int release = max(-count, (int)diff); 637 count += diff; 638 for(release) { 639 unpark( pop_head( waiting ) ); 640 } 641 642 unlock( lock ); 643 644 return thrd != 0p; 645 } 646 652 size_t V( semaphore & sem, size_t count ) with( sem ) { 653 lock( lock$ __cfaabi_dbg_ctx2 ); 654 size_t release = max( (size_t)-count$, count ); 655 count$ += count; 656 for ( release ) unpark( pop_head( waiting$ ) ); 657 unlock( lock$ ); 658 return release; 659 } -
libcfa/src/concurrency/locks.hfa
r8f448e0 r5e0b6657 11 11 // Created On : Thu Jan 21 19:46:50 2021 12 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Thu Aug 21 22:36:44202514 // Update Count : 2313 // Last Modified On : Sun Nov 23 22:38:45 2025 14 // Update Count : 67 15 15 // 16 16 … … 81 81 82 82 83 84 // -----------------------------------------------------------------------------85 // Semaphore 83 //----------------------------------------------------------------------------- 84 // Semaphore, counting 85 86 86 struct semaphore { 87 __spinlock_t lock; 88 int count; 89 __queue_t( thread$) waiting; 90 }; 91 92 void ?{}( semaphore & this, int count = 1 ); 93 void ^?{}( semaphore & this ); 94 bool P( semaphore & this ); 95 bool V( semaphore & this ); 96 bool V( semaphore & this, unsigned count ); 97 thread$ * V( semaphore & this, bool ); 87 ssize_t count$; // - => # waiting threads, 0 => block, + => acquire 88 __spinlock_t lock$; // protect blocking-lock critical sections 89 __queue_t( thread$ ) waiting$; // waiting threads 90 }; 91 92 void ?{}( semaphore & sem, ssize_t count = 1 ); 93 // Return values are used for composition. Calling threads know if a thread is unblocked, which can be useful for 94 // debugging, performance instrumentation and other metadata tracking purposes. 95 bool P( semaphore & sem ); 96 static inline bool P( semaphore & sem, uintptr_t shadow ) { active_thread()->shadow$ = shadow; return P( sem ); } 97 bool P( semaphore & sem, semaphore & lock ); // atomic block and release 98 bool try_P( semaphore & sem ); 99 static inline bool P( semaphore & sem, semaphore & lock, uintptr_t shadow ) { active_thread()->shadow$ = shadow; return P( sem, lock ); } 100 bool V( semaphore & sem ); 101 size_t V( semaphore & sem, size_t count ); 102 static inline uintptr_t front( semaphore & sem ) with( sem ) { // return shadow information for first waiting thread 103 #ifdef __CFA_DEBUG__ 104 if ( waiting$ ) { // condition queue must not be empty 105 abort( "Attempt to access user data on an empty semaphore lock.\n" 106 "Possible cause is not checking if the condition lock is empty before reading stored data." ); 107 } // if 108 #endif // __CFA_DEBUG__ 109 return waiting$.head->shadow$; // return condition information stored with blocked task 110 } 111 static inline ssize_t counter( semaphore & sem ) with( sem ) { return count$; } // - => # waiting threads, 0 => block, + => acquire 112 //static inline int ?!=?( semaphore & sem, zero_t ) with( sem ) { return count$ != 0; } // empty waiting queue 113 static inline bool empty( semaphore & sem ) with( sem ) { return count$ == 0; } // empty waiting queue 114 98 115 99 116 //---------- … … 110 127 static inline void on_wakeup( single_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); } 111 128 static inline void on_notify( single_acquisition_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); } 112 static inline bool register_select ( single_acquisition_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }113 static inline bool unregister_select ( single_acquisition_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }114 static inline bool on_selected ( single_acquisition_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }129 static inline bool register_select$( single_acquisition_lock & this, select_node & node ) { return register_select$( (blocking_lock &)this, node ); } 130 static inline bool unregister_select$( single_acquisition_lock & this, select_node & node ) { return unregister_select$( (blocking_lock &)this, node ); } 131 static inline bool on_selected$( single_acquisition_lock & this, select_node & node ) { return on_selected$( (blocking_lock &)this, node ); } 115 132 __CFA_SELECT_GET_TYPE( single_acquisition_lock ); 116 133 … … 128 145 static inline void on_wakeup( owner_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); } 129 146 static inline void on_notify( owner_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); } 130 static inline bool register_select ( owner_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }131 static inline bool unregister_select ( owner_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }132 static inline bool on_selected ( owner_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }147 static inline bool register_select$( owner_lock & this, select_node & node ) { return register_select$( (blocking_lock &)this, node ); } 148 static inline bool unregister_select$( owner_lock & this, select_node & node ) { return unregister_select$( (blocking_lock &)this, node ); } 149 static inline bool on_selected$( owner_lock & this, select_node & node ) { return on_selected$( (blocking_lock &)this, node ); } 133 150 __CFA_SELECT_GET_TYPE( owner_lock ); 134 151 … … 594 611 595 612 // waituntil() support 596 static inline bool register_select ( simple_owner_lock & this, select_node & node ) with( this ) {613 static inline bool register_select$( simple_owner_lock & this, select_node & node ) with( this ) { 597 614 lock( lock __cfaabi_dbg_ctx2 ); 598 615 … … 626 643 } 627 644 628 static inline bool unregister_select ( simple_owner_lock & this, select_node & node ) with( this ) {645 static inline bool unregister_select$( simple_owner_lock & this, select_node & node ) with( this ) { 629 646 lock( lock __cfaabi_dbg_ctx2 ); 630 647 if ( isListed( node ) ) { … … 644 661 } 645 662 646 static inline bool on_selected ( simple_owner_lock & /*this*/, select_node & /*node*/ ) { return true; }663 static inline bool on_selected$( simple_owner_lock & /*this*/, select_node & /*node*/ ) { return true; } 647 664 __CFA_SELECT_GET_TYPE( simple_owner_lock ); 648 665 -
libcfa/src/concurrency/monitor.cfa
r8f448e0 r5e0b6657 10 10 // Created On : Thd Feb 23 12:27:26 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Apr 25 07:20:22202513 // Update Count : 8 012 // Last Modified On : Tue Nov 4 22:03:41 2025 13 // Update Count : 82 14 14 // 15 15 … … 494 494 495 495 bool signal( condition & this ) libcfa_public { 496 if ( is_empty( this ) ) { return false; }496 if ( empty( this ) ) { return false; } 497 497 498 498 //Check that everything is as expected … … 581 581 // Access the user_info of the thread waiting at the front of the queue 582 582 uintptr_t front( condition & this ) libcfa_public { 583 verifyf( ! is_empty(this),583 verifyf( ! empty( this ), 584 584 "Attempt to access user data on an empty condition.\n" 585 585 "Possible cause is not checking if the condition is empty before reading stored data." -
libcfa/src/concurrency/monitor.hfa
r8f448e0 r5e0b6657 10 10 // Created On : Thd Feb 23 12:27:26 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T hu Feb 2 11:29:21 202313 // Update Count : 1 212 // Last Modified On : Tue Nov 4 21:56:17 2025 13 // Update Count : 16 14 14 // 15 15 … … 146 146 } 147 147 148 void wait ( condition & this, uintptr_t user_info = 0 ); 149 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; } 150 bool signal ( condition & this ); 151 bool signal_block( condition & this ); 152 static inline bool signal_all ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; } 153 uintptr_t front ( condition & this ); 148 void wait( condition & this, uintptr_t user_info = 0 ); 149 static inline bool empty( condition & this ) { return this.blocked.head == 1p; } 150 bool signal ( condition & this ); 151 bool signal_block( condition & this ); 152 static inline bool signal_all( condition & this ) { 153 bool ret = false; 154 while ( !empty( this ) ) { 155 ret = signal(this) || ret; 156 } 157 return ret; 158 } 159 uintptr_t front( condition & this ); 154 160 155 161 //----------------------------------------------------------------------------- -
libcfa/src/concurrency/select.cfa
r8f448e0 r5e0b6657 10 10 // Author : Colby Alexander Parsons 11 11 // Created On : Thu Apr 21 19:46:50 2023 12 // Last Modified By : Kyoung Seo13 // Last Modified On : Wed Mar 19 12:00:00202514 // Update Count : 112 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Sun Nov 23 22:38:53 2025 14 // Update Count : 4 15 15 // 16 16 … … 39 39 40 40 // Selectable trait routines 41 bool register_select ( select_timeout_node & this, select_node & node ) {41 bool register_select$( select_timeout_node & this, select_node & node ) { 42 42 this.s_node = &node; 43 43 node.extra = 0p; … … 45 45 return false; 46 46 } 47 bool unregister_select ( select_timeout_node & this, select_node & node ) {47 bool unregister_select$( select_timeout_node & this, select_node & node ) { 48 48 unregister_self( &this.a_node ); 49 49 return false; 50 50 } 51 bool on_selected ( select_timeout_node & this, select_node & node ) { return true; }51 bool on_selected$( select_timeout_node & this, select_node & node ) { return true; } 52 52 -
libcfa/src/concurrency/select.hfa
r8f448e0 r5e0b6657 11 11 // Created On : Thu Jan 21 19:46:50 2023 12 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Fri Apr 25 07:31:26 202514 // Update Count : 513 // Last Modified On : Sun Nov 23 22:38:36 2025 14 // Update Count : 8 15 15 // 16 16 … … 87 87 // For registering a select stmt on a selectable concurrency primitive 88 88 // Returns bool that indicates if operation is already SAT 89 bool register_select ( T &, select_node & );89 bool register_select$( T &, select_node & ); 90 90 91 91 // For unregistering a select stmt on a selectable concurrency primitive 92 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) 93 bool unregister_select ( T &, select_node & );93 bool unregister_select$( T &, select_node & ); 94 94 95 95 // This routine is run on the selecting thread prior to executing the statement corresponding to the select_node 96 96 // passed as an arg to this routine. If true is returned proceed as normal, if false is returned the statement is skipped 97 bool on_selected ( T &, select_node & );97 bool on_selected$( T &, select_node & ); 98 98 }; 99 99 // Used inside the compiler to allow for overloading on return type for operations such as '?<<?' for channels … … 209 209 210 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 );213 bool on_selected ( select_timeout_node & this, select_node & node );211 bool register_select$( select_timeout_node & this, select_node & node ); 212 bool unregister_select$( select_timeout_node & this, select_node & node ); 213 bool on_selected$( select_timeout_node & this, select_node & node ); 214 214 select_timeout_node __CFA_select_get_type( select_timeout_node this ); 215 215 -
libcfa/src/strstream.cfa
r8f448e0 r5e0b6657 10 10 // Created On : Thu Apr 22 22:24:35 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Apr 14 20:45:00202513 // Update Count : 11 612 // Last Modified On : Sat Oct 11 15:10:56 2025 13 // Update Count : 119 14 14 // 15 15 … … 148 148 abort(); 149 149 } // if 150 os.cursor$ = 0; 150 151 return os; 151 152 } // write … … 155 156 } // write 156 157 158 void clear( ostrstream & os ) { 159 os.cursor$ = 0; 160 } // clear 157 161 158 162 // *********************************** istrstream *********************************** -
libcfa/src/strstream.hfa
r8f448e0 r5e0b6657 10 10 // Created On : Thu Apr 22 22:20:59 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Apr 14 20:57:15202513 // Update Count : 6 112 // Last Modified On : Sat Oct 11 15:11:06 2025 13 // Update Count : 62 14 14 // 15 15 … … 75 75 void ?{}( ostrstream &, char buf[], size_t size ); 76 76 77 void clear( ostrstream & os ); 78 77 79 78 80 // *********************************** istrstream ***********************************
Note:
See TracChangeset
for help on using the changeset viewer.