Changeset a77496cb
- Timestamp:
- Sep 1, 2020, 4:58:13 PM (4 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 70cab43
- Parents:
- 25a1cb0
- Files:
-
- 2 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/bits/locks.hfa
r25a1cb0 ra77496cb 218 218 } 219 219 220 // Semaphore which only supports a single thread and one post 221 // Semaphore which only supports a single thread 220 // Synchronozation primitive which only supports a single thread and one post 221 // Similar to a binary semaphore with a 'one shot' semantic 222 // is expected to be discarded after each party call their side 222 223 struct oneshot { 224 // Internal state : 225 // 0p : is initial state (wait will block) 226 // 1p : fulfilled (wait won't block) 227 // any thread : a thread is currently waiting 223 228 struct $thread * volatile ptr; 224 229 }; … … 231 236 void ^?{}(oneshot & this) {} 232 237 238 // Wait for the post, return immidiately if it already happened. 239 // return true if the thread was parked 233 240 bool wait(oneshot & this) { 234 241 for() { … … 244 251 } 245 252 253 // Mark as fulfilled, wake thread if needed 254 // return true if a thread was unparked 246 255 bool post(oneshot & this) { 247 256 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); … … 251 260 } 252 261 } 262 263 // base types for future to build upon 264 // It is based on the 'oneshot' type to allow multiple futures 265 // to block on the same instance, permitting users to block a single 266 // thread on "any of" [a given set of] futures. 267 // does not support multiple threads waiting on the same future 268 struct future_t { 269 // Internal state : 270 // 0p : is initial state (wait will block) 271 // 1p : fulfilled (wait won't block) 272 // 2p : in progress () 273 // 3p : abandoned, server should delete 274 // any oneshot : a context has been setup to wait, a thread could wait on it 275 struct oneshot * volatile ptr; 276 }; 277 278 static inline { 279 void ?{}(future_t & this) { 280 this.ptr = 0p; 281 } 282 283 void ^?{}(future_t & this) {} 284 285 // check if the future is available 286 bool available( future_t & this ) { 287 return this.ptr == 1p; 288 } 289 290 // Prepare the future to be waited on 291 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 292 bool setup( future_t & this, oneshot & wait_ctx ) { 293 /* paranoid */ verify( wait_ctx.ptr == 0p ); 294 // The future needs to set the wait context 295 for() { 296 struct oneshot * expected = this.ptr; 297 // Is the future already fulfilled? 298 if(expected == 1p) return false; // Yes, just return false (didn't block) 299 300 // The future is not fulfilled, try to setup the wait context 301 /* paranoid */ verify( expected == 0p ); 302 if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 303 return true; 304 } 305 } 306 } 307 308 // Stop waiting on a future 309 // When multiple futures are waited for together in "any of" pattern 310 // futures that weren't fulfilled before the thread woke up 311 // should retract the wait ctx 312 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 313 void retract( future_t & this, oneshot & wait_ctx ) { 314 // Remove the wait context 315 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 316 317 // got == 0p: future was never actually setup, just return 318 if( got == 0p ) return; 319 320 // got == wait_ctx: since fulfil does an atomic_swap, 321 // if we got back the original then no one else saw context 322 // It is safe to delete (which could happen after the return) 323 if( got == &wait_ctx ) return; 324 325 // got == 1p: the future is ready and the context was fully consumed 326 // the server won't use the pointer again 327 // It is safe to delete (which could happen after the return) 328 if( got == 1p ) return; 329 330 // got == 2p: the future is ready but the context hasn't fully been consumed 331 // spin until it is safe to move on 332 if( got == 2p ) { 333 while( this.ptr != 1p ) Pause(); 334 return; 335 } 336 337 // got == any thing else, something wen't wrong here, abort 338 abort("Future in unexpected state"); 339 } 340 341 // Mark the future as abandoned, meaning it will be deleted by the server 342 void abandon( future_t & this ) { 343 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST); 344 345 // got == 2p: the future is ready but the context hasn't fully been consumed 346 // spin until it is safe to move on 347 if( got == 2p ) { 348 while( this.ptr != 1p ) Pause(); 349 } 350 return; 351 } 352 353 // from the server side, mark the future as fulfilled 354 // delete it if needed 355 bool fulfil( future_t & this ) { 356 for() { 357 struct oneshot * expected = this.ptr; 358 // was this abandoned? 359 if( expected == 3p ) { free( &this ); return false; } 360 361 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen 362 /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 363 364 // If there is a wait context, we need to consume it and mark it as consumed after 365 // If there is no context then we can skip the in progress phase 366 struct oneshot * want = expected == 0p ? 1p : 2p; 367 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 368 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; } 369 bool ret = post( *expected ); 370 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 371 return ret; 372 } 373 } 374 375 } 376 377 // Wait for the future to be fulfilled 378 bool wait( future_t & this ) { 379 oneshot temp; 380 if( !setup(this, temp) ) return false; 381 382 // Wait context is setup, just wait on it 383 bool ret = wait( temp ); 384 385 // Wait for the future to tru 386 while( this.ptr == 2p ) Pause(); 387 // Make sure the state makes sense 388 // Should be fulfilled, could be in progress but it's out of date if so 389 // since if that is the case, the oneshot was fulfilled (unparking this thread) 390 // and the oneshot should not be needed any more 391 __attribute__((unused)) struct oneshot * was = this.ptr; 392 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was ); 393 394 // Mark the future as fulfilled, to be consistent 395 // with potential calls to avail 396 // this.ptr = 1p; 397 return ret; 398 } 399 } 253 400 #endif
Note: See TracChangeset
for help on using the changeset viewer.