Changeset 1c7ed2d for libcfa/src/concurrency
- Timestamp:
- Sep 19, 2022, 3:54:38 PM (2 years ago)
- Branches:
- ADT, ast-experimental, master, pthread-emulation
- Children:
- ae1d151
- Parents:
- 9d55ff6
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/kernel/fwd.hfa
r9d55ff6 r1c7ed2d 179 179 // Similar to a binary semaphore with a 'one shot' semantic 180 180 // is expected to be discarded after each party call their side 181 enum(struct thread$ *) { oneshot_ARMED = 0p, oneshot_FULFILLED = 1p }; 181 182 struct oneshot { 182 183 // Internal state : 183 // 0p : is initial state (wait will block)184 // 1p : fulfilled (wait won't block)184 // armed : initial state, wait will block 185 // fulfilled : wait won't block 185 186 // any thread : a thread is currently waiting 186 187 struct thread$ * volatile ptr; … … 189 190 static inline { 190 191 void ?{}(oneshot & this) { 191 this.ptr = 0p;192 this.ptr = oneshot_ARMED; 192 193 } 193 194 … … 199 200 for() { 200 201 struct thread$ * expected = this.ptr; 201 if(expected == 1p) return false;202 if(expected == oneshot_FULFILLED) return false; 202 203 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 203 204 park(); 204 /* paranoid */ verify( this.ptr == 1p);205 /* paranoid */ verify( this.ptr == oneshot_FULFILLED ); 205 206 return true; 206 207 } … … 211 212 // return true if a thread was unparked 212 213 thread$ * post(oneshot & this, bool do_unpark = true) { 213 struct thread$ * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);214 if( got == 0p || got == 1p) return 0p;214 struct thread$ * got = __atomic_exchange_n( &this.ptr, oneshot_FULFILLED, __ATOMIC_SEQ_CST); 215 if( got == oneshot_ARMED || got == oneshot_FULFILLED ) return 0p; 215 216 if(do_unpark) unpark( got ); 216 217 return got; … … 223 224 // thread on "any of" [a given set of] futures. 224 225 // does not support multiple threads waiting on the same future 226 enum(struct oneshot *) { future_ARMED = 0p, future_FULFILLED = 1p, future_PROGRESS = 2p, future_ABANDONED = 3p }; 225 227 struct future_t { 226 228 // Internal state : 227 // 0p : is initial state (wait will block)228 // 1p : fulfilled (wait won't block)229 // 2p : in progress ()230 // 3p : abandoned, server should delete229 // armed : initial state, wait will block 230 // fulfilled : result is ready, wait won't block 231 // progress : someone else is in the process of fulfilling this 232 // abandoned : client no longer cares, server should delete 231 233 // any oneshot : a context has been setup to wait, a thread could wait on it 232 234 struct oneshot * volatile ptr; … … 235 237 static inline { 236 238 void ?{}(future_t & this) { 237 this.ptr = 0p;239 this.ptr = future_ARMED; 238 240 } 239 241 … … 242 244 void reset(future_t & this) { 243 245 // needs to be in 0p or 1p 244 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);246 __atomic_exchange_n( &this.ptr, future_ARMED, __ATOMIC_SEQ_CST); 245 247 } 246 248 247 249 // check if the future is available 248 250 bool available( future_t & this ) { 249 while( this.ptr == 2p) Pause();250 return this.ptr == 1p;251 while( this.ptr == future_PROGRESS ) Pause(); 252 return this.ptr == future_FULFILLED; 251 253 } 252 254 … … 254 256 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 255 257 bool setup( future_t & this, oneshot & wait_ctx ) { 256 /* paranoid */ verify( wait_ctx.ptr == 0p || wait_ctx.ptr == 1p);258 /* paranoid */ verify( wait_ctx.ptr == oneshot_ARMED || wait_ctx.ptr == oneshot_FULFILLED ); 257 259 // The future needs to set the wait context 258 260 for() { 259 261 struct oneshot * expected = this.ptr; 260 262 // Is the future already fulfilled? 261 if(expected == 1p) return false; // Yes, just return false (didn't block)263 if(expected == future_FULFILLED) return false; // Yes, just return false (didn't block) 262 264 263 265 // The future is not fulfilled, try to setup the wait context … … 277 279 278 280 // attempt to remove the context so it doesn't get consumed. 279 if(__atomic_compare_exchange_n( &this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {281 if(__atomic_compare_exchange_n( &this.ptr, &expected, future_ARMED, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 280 282 // we still have the original context, then no one else saw it 281 283 return false; 282 284 } 283 285 284 // expected == 0p: future was never actually setup, just return285 if( expected == 0p) return false;286 287 // expected == 1p: the future is ready and the context was fully consumed286 // expected == ARMED: future was never actually setup, just return 287 if( expected == future_ARMED ) return false; 288 289 // expected == FULFILLED: the future is ready and the context was fully consumed 288 290 // the server won't use the pointer again 289 291 // It is safe to delete (which could happen after the return) 290 if( expected == 1p) return true;291 292 // expected == 2p: the future is ready but the context hasn't fully been consumed292 if( expected == future_FULFILLED ) return true; 293 294 // expected == PROGRESS: the future is ready but the context hasn't fully been consumed 293 295 // spin until it is safe to move on 294 if( expected == 2p) {295 while( this.ptr != 1p) Pause();296 /* paranoid */ verify( this.ptr == 1p);296 if( expected == future_PROGRESS ) { 297 while( this.ptr != future_FULFILLED ) Pause(); 298 /* paranoid */ verify( this.ptr == future_FULFILLED ); 297 299 return true; 298 300 } … … 305 307 // Mark the future as abandoned, meaning it will be deleted by the server 306 308 bool abandon( future_t & this ) { 307 /* paranoid */ verify( this.ptr != 3p);309 /* paranoid */ verify( this.ptr != future_ABANDONED ); 308 310 309 311 // Mark the future as abandonned 310 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);312 struct oneshot * got = __atomic_exchange_n( &this.ptr, future_ABANDONED, __ATOMIC_SEQ_CST); 311 313 312 314 // If the future isn't already fulfilled, let the server delete it 313 if( got == 0p) return false;314 315 // got == 2p: the future is ready but the context hasn't fully been consumed315 if( got == future_ARMED ) return false; 316 317 // got == PROGRESS: the future is ready but the context hasn't fully been consumed 316 318 // spin until it is safe to move on 317 if( got == 2p) {318 while( this.ptr != 1p) Pause();319 got = 1p;319 if( got == future_PROGRESS ) { 320 while( this.ptr != future_FULFILLED ) Pause(); 321 got = future_FULFILLED; 320 322 } 321 323 322 324 // The future is completed delete it now 323 /* paranoid */ verify( this.ptr != 1p);325 /* paranoid */ verify( this.ptr != future_FULFILLED ); 324 326 free( &this ); 325 327 return true; … … 336 338 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 337 339 #endif 338 if( expected == 3p) { free( &this ); return 0p; }340 if( expected == future_ABANDONED ) { free( &this ); return 0p; } 339 341 #if defined(__GNUC__) && __GNUC__ >= 7 340 342 #pragma GCC diagnostic pop 341 343 #endif 342 344 343 /* paranoid */ verify( expected != 1p); // Future is already fulfilled, should not happen344 /* paranoid */ verify( expected != 2p); // Future is bein fulfilled by someone else, this is even less supported then the previous case.345 /* paranoid */ verify( expected != future_FULFILLED ); // Future is already fulfilled, should not happen 346 /* paranoid */ verify( expected != future_PROGRESS ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 345 347 346 348 // If there is a wait context, we need to consume it and mark it as consumed after 347 349 // If there is no context then we can skip the in progress phase 348 struct oneshot * want = expected == 0p ? 1p : 2p;350 struct oneshot * want = expected == future_ARMED ? future_FULFILLED : future_PROGRESS; 349 351 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 350 if( expected == 0p) { return 0p; }352 if( expected == future_ARMED ) { return 0p; } 351 353 thread$ * ret = post( *expected, do_unpark ); 352 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);354 __atomic_store_n( &this.ptr, future_FULFILLED, __ATOMIC_SEQ_CST); 353 355 return ret; 354 356 } … … 366 368 367 369 // Wait for the future to tru 368 while( this.ptr == 2p) Pause();370 while( this.ptr == future_PROGRESS ) Pause(); 369 371 // Make sure the state makes sense 370 372 // Should be fulfilled, could be in progress but it's out of date if so … … 372 374 // and the oneshot should not be needed any more 373 375 __attribute__((unused)) struct oneshot * was = this.ptr; 374 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was );376 /* paranoid */ verifyf( was == future_FULFILLED, "Expected this.ptr to be 1p, was %p\n", was ); 375 377 376 378 // Mark the future as fulfilled, to be consistent
Note: See TracChangeset
for help on using the changeset viewer.