diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-08 16:57:53 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-08 17:00:56 +0200 |
| commit | 96daea993eeea17f0c64325491943e48795ff751 (patch) | |
| tree | 653072dc81d0360382576b520e19619b0e06d085 /src/threading.cpp | |
| parent | 63b930ebc5060efecea0ec3acf7e5c93e4047864 (diff) | |
| download | lanes-96daea993eeea17f0c64325491943e48795ff751.tar.gz lanes-96daea993eeea17f0c64325491943e48795ff751.tar.bz2 lanes-96daea993eeea17f0c64325491943e48795ff751.zip | |
C++ migration: use std::jthread, std::condition_variable, std::chrono.
win32 pthread support is gone
new setting configure.shutdown_mode for cancellation of free-running threads at shutdown.
no more hard thread termination! If a thread doesn't cooperate, an error is raised.
lane.status "killed" is gone
lane:cancel can't force-kill.
Diffstat (limited to 'src/threading.cpp')
| -rw-r--r-- | src/threading.cpp | 525 |
1 files changed, 26 insertions, 499 deletions
diff --git a/src/threading.cpp b/src/threading.cpp index afeb184..fc20931 100644 --- a/src/threading.cpp +++ b/src/threading.cpp | |||
| @@ -93,9 +93,6 @@ THE SOFTWARE. | |||
| 93 | # pragma warning( disable : 4054 ) | 93 | # pragma warning( disable : 4054 ) |
| 94 | #endif | 94 | #endif |
| 95 | 95 | ||
| 96 | //#define THREAD_CREATE_RETRIES_MAX 20 | ||
| 97 | // loops (maybe retry forever?) | ||
| 98 | |||
| 99 | /* | 96 | /* |
| 100 | * FAIL is for unexpected API return values - essentially programming | 97 | * FAIL is for unexpected API return values - essentially programming |
| 101 | * error in _this_ code. | 98 | * error in _this_ code. |
| @@ -196,36 +193,6 @@ time_d now_secs(void) { | |||
| 196 | } | 193 | } |
| 197 | 194 | ||
| 198 | 195 | ||
| 199 | /* | ||
| 200 | */ | ||
| 201 | time_d SIGNAL_TIMEOUT_PREPARE( double secs ) { | ||
| 202 | if (secs<=0.0) return secs; | ||
| 203 | else return now_secs() + secs; | ||
| 204 | } | ||
| 205 | |||
| 206 | |||
| 207 | #if THREADAPI == THREADAPI_PTHREAD | ||
| 208 | /* | ||
| 209 | * Prepare 'abs_secs' kind of timeout to 'timespec' format | ||
| 210 | */ | ||
| 211 | static void prepare_timeout( struct timespec *ts, time_d abs_secs ) { | ||
| 212 | assert(ts); | ||
| 213 | assert( abs_secs >= 0.0 ); | ||
| 214 | |||
| 215 | if (abs_secs==0.0) | ||
| 216 | abs_secs= now_secs(); | ||
| 217 | |||
| 218 | ts->tv_sec= (time_t) floor( abs_secs ); | ||
| 219 | ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL; // 1ms = 1000000ns | ||
| 220 | if (ts->tv_nsec == 1000000000UL) | ||
| 221 | { | ||
| 222 | ts->tv_nsec = 0; | ||
| 223 | ts->tv_sec = ts->tv_sec + 1; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | #endif // THREADAPI == THREADAPI_PTHREAD | ||
| 227 | |||
| 228 | |||
| 229 | /*---=== Threading ===---*/ | 196 | /*---=== Threading ===---*/ |
| 230 | 197 | ||
| 231 | //--- | 198 | //--- |
| @@ -268,30 +235,6 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) { | |||
| 268 | 235 | ||
| 269 | #if THREADAPI == THREADAPI_WINDOWS | 236 | #if THREADAPI == THREADAPI_WINDOWS |
| 270 | 237 | ||
| 271 | #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available | ||
| 272 | // | ||
| 273 | void MUTEX_INIT( MUTEX_T *ref ) { | ||
| 274 | *ref= CreateMutex( nullptr /*security attr*/, false /*not locked*/, nullptr ); | ||
| 275 | if (!ref) FAIL( "CreateMutex", GetLastError() ); | ||
| 276 | } | ||
| 277 | void MUTEX_FREE( MUTEX_T *ref ) { | ||
| 278 | if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() ); | ||
| 279 | *ref= nullptr; | ||
| 280 | } | ||
| 281 | void MUTEX_LOCK( MUTEX_T *ref ) | ||
| 282 | { | ||
| 283 | DWORD rc = WaitForSingleObject( *ref, INFINITE); | ||
| 284 | // ERROR_WAIT_NO_CHILDREN means a thread was killed (lane terminated because of error raised during a linda transfer for example) while having grabbed this mutex | ||
| 285 | // this is not a big problem as we will grab it just the same, so ignore this particular error | ||
| 286 | if( rc != 0 && rc != ERROR_WAIT_NO_CHILDREN) | ||
| 287 | FAIL( "WaitForSingleObject", (rc == WAIT_FAILED) ? GetLastError() : rc); | ||
| 288 | } | ||
| 289 | void MUTEX_UNLOCK( MUTEX_T *ref ) { | ||
| 290 | if (!ReleaseMutex(*ref)) | ||
| 291 | FAIL( "ReleaseMutex", GetLastError() ); | ||
| 292 | } | ||
| 293 | #endif // CONDITION_VARIABLE aren't available | ||
| 294 | |||
| 295 | static int const gs_prio_remap[] = | 238 | static int const gs_prio_remap[] = |
| 296 | { | 239 | { |
| 297 | THREAD_PRIORITY_IDLE, | 240 | THREAD_PRIORITY_IDLE, |
| @@ -303,37 +246,7 @@ static int const gs_prio_remap[] = | |||
| 303 | THREAD_PRIORITY_TIME_CRITICAL | 246 | THREAD_PRIORITY_TIME_CRITICAL |
| 304 | }; | 247 | }; |
| 305 | 248 | ||
| 306 | /* MSDN: "If you would like to use the CRT in ThreadProc, use the | 249 | // ############################################################################################### |
| 307 | _beginthreadex function instead (of CreateThread)." | ||
| 308 | MSDN: "you can create at most 2028 threads" | ||
| 309 | */ | ||
| 310 | // Note: Visual C++ requires '__stdcall' where it is | ||
| 311 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */) | ||
| 312 | { | ||
| 313 | HANDLE h = (HANDLE) _beginthreadex(nullptr, // security | ||
| 314 | _THREAD_STACK_SIZE, | ||
| 315 | func, | ||
| 316 | data, | ||
| 317 | 0, // flags (0/CREATE_SUSPENDED) | ||
| 318 | nullptr // thread id (not used) | ||
| 319 | ); | ||
| 320 | |||
| 321 | if (h == nullptr) // _beginthreadex returns 0L on failure instead of -1L (like _beginthread) | ||
| 322 | { | ||
| 323 | FAIL( "CreateThread", GetLastError()); | ||
| 324 | } | ||
| 325 | |||
| 326 | if (prio != THREAD_PRIO_DEFAULT) | ||
| 327 | { | ||
| 328 | if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) | ||
| 329 | { | ||
| 330 | FAIL( "SetThreadPriority", GetLastError()); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | *ref = h; | ||
| 335 | } | ||
| 336 | |||
| 337 | 250 | ||
| 338 | void THREAD_SET_PRIORITY( int prio) | 251 | void THREAD_SET_PRIORITY( int prio) |
| 339 | { | 252 | { |
| @@ -344,42 +257,26 @@ void THREAD_SET_PRIORITY( int prio) | |||
| 344 | } | 257 | } |
| 345 | } | 258 | } |
| 346 | 259 | ||
| 347 | void THREAD_SET_AFFINITY( unsigned int aff) | 260 | // ############################################################################################### |
| 261 | |||
| 262 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_) | ||
| 348 | { | 263 | { |
| 349 | if( !SetThreadAffinityMask( GetCurrentThread(), aff)) | 264 | // prio range [-3,+3] was checked by the caller |
| 265 | if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) | ||
| 350 | { | 266 | { |
| 351 | FAIL( "THREAD_SET_AFFINITY", GetLastError()); | 267 | FAIL("JTHREAD_SET_PRIORITY", GetLastError()); |
| 352 | } | 268 | } |
| 353 | } | 269 | } |
| 354 | 270 | ||
| 355 | bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | 271 | // ############################################################################################### |
| 356 | { | ||
| 357 | DWORD ms = (secs<0.0) ? INFINITE : (DWORD)((secs*1000.0)+0.5); | ||
| 358 | 272 | ||
| 359 | DWORD rc= WaitForSingleObject( *ref, ms /*timeout*/ ); | 273 | void THREAD_SET_AFFINITY(unsigned int aff) |
| 360 | // | 274 | { |
| 361 | // (WAIT_ABANDONED) | 275 | if( !SetThreadAffinityMask( GetCurrentThread(), aff)) |
| 362 | // WAIT_OBJECT_0 success (0) | ||
| 363 | // WAIT_TIMEOUT | ||
| 364 | // WAIT_FAILED more info via GetLastError() | ||
| 365 | |||
| 366 | if (rc == WAIT_TIMEOUT) return false; | ||
| 367 | if( rc !=0) FAIL( "WaitForSingleObject", rc==WAIT_FAILED ? GetLastError() : rc); | ||
| 368 | *ref = nullptr; // thread no longer usable | ||
| 369 | return true; | ||
| 370 | } | ||
| 371 | // | ||
| 372 | void THREAD_KILL( THREAD_T *ref ) | ||
| 373 | { | 276 | { |
| 374 | // nonexistent on Xbox360, simply disable until a better solution is found | 277 | FAIL( "THREAD_SET_AFFINITY", GetLastError()); |
| 375 | #if !defined( PLATFORM_XBOX) | ||
| 376 | // in theory no-one should call this as it is very dangerous (memory and mutex leaks, no notification of DLLs, etc.) | ||
| 377 | if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError()); | ||
| 378 | #endif // PLATFORM_XBOX | ||
| 379 | *ref = nullptr; | ||
| 380 | } | 278 | } |
| 381 | 279 | } | |
| 382 | void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want | ||
| 383 | 280 | ||
| 384 | #if !defined __GNUC__ | 281 | #if !defined __GNUC__ |
| 385 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | 282 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
| @@ -414,158 +311,6 @@ bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | |||
| 414 | #endif // !__GNUC__ | 311 | #endif // !__GNUC__ |
| 415 | } | 312 | } |
| 416 | 313 | ||
| 417 | #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available | ||
| 418 | |||
| 419 | void SIGNAL_INIT( SIGNAL_T* ref) | ||
| 420 | { | ||
| 421 | InitializeCriticalSection( &ref->signalCS); | ||
| 422 | InitializeCriticalSection( &ref->countCS); | ||
| 423 | if( 0 == (ref->waitEvent = CreateEvent( 0, true, false, 0))) // manual-reset | ||
| 424 | FAIL( "CreateEvent", GetLastError()); | ||
| 425 | if( 0 == (ref->waitDoneEvent = CreateEvent( 0, false, false, 0))) // auto-reset | ||
| 426 | FAIL( "CreateEvent", GetLastError()); | ||
| 427 | ref->waitersCount = 0; | ||
| 428 | } | ||
| 429 | |||
| 430 | void SIGNAL_FREE( SIGNAL_T* ref) | ||
| 431 | { | ||
| 432 | CloseHandle( ref->waitDoneEvent); | ||
| 433 | CloseHandle( ref->waitEvent); | ||
| 434 | DeleteCriticalSection( &ref->countCS); | ||
| 435 | DeleteCriticalSection( &ref->signalCS); | ||
| 436 | } | ||
| 437 | |||
| 438 | bool SIGNAL_WAIT( SIGNAL_T* ref, MUTEX_T* mu_ref, time_d abs_secs) | ||
| 439 | { | ||
| 440 | DWORD errc; | ||
| 441 | DWORD ms; | ||
| 442 | |||
| 443 | if( abs_secs < 0.0) | ||
| 444 | ms = INFINITE; | ||
| 445 | else if( abs_secs == 0.0) | ||
| 446 | ms = 0; | ||
| 447 | else | ||
| 448 | { | ||
| 449 | time_d msd = (abs_secs - now_secs()) * 1000.0 + 0.5; | ||
| 450 | // If the time already passed, still try once (ms==0). A short timeout | ||
| 451 | // may have turned negative or 0 because of the two time samples done. | ||
| 452 | ms = msd <= 0.0 ? 0 : (DWORD)msd; | ||
| 453 | } | ||
| 454 | |||
| 455 | EnterCriticalSection( &ref->signalCS); | ||
| 456 | EnterCriticalSection( &ref->countCS); | ||
| 457 | ++ ref->waitersCount; | ||
| 458 | LeaveCriticalSection( &ref->countCS); | ||
| 459 | LeaveCriticalSection( &ref->signalCS); | ||
| 460 | |||
| 461 | errc = SignalObjectAndWait( *mu_ref, ref->waitEvent, ms, false); | ||
| 462 | |||
| 463 | EnterCriticalSection( &ref->countCS); | ||
| 464 | if( 0 == -- ref->waitersCount) | ||
| 465 | { | ||
| 466 | // we're the last one leaving... | ||
| 467 | ResetEvent( ref->waitEvent); | ||
| 468 | SetEvent( ref->waitDoneEvent); | ||
| 469 | } | ||
| 470 | LeaveCriticalSection( &ref->countCS); | ||
| 471 | MUTEX_LOCK( mu_ref); | ||
| 472 | |||
| 473 | switch( errc) | ||
| 474 | { | ||
| 475 | case WAIT_TIMEOUT: | ||
| 476 | return false; | ||
| 477 | case WAIT_OBJECT_0: | ||
| 478 | return true; | ||
| 479 | } | ||
| 480 | |||
| 481 | FAIL( "SignalObjectAndWait", GetLastError()); | ||
| 482 | return false; | ||
| 483 | } | ||
| 484 | |||
| 485 | void SIGNAL_ALL( SIGNAL_T* ref) | ||
| 486 | { | ||
| 487 | DWORD errc = WAIT_OBJECT_0; | ||
| 488 | |||
| 489 | EnterCriticalSection( &ref->signalCS); | ||
| 490 | EnterCriticalSection( &ref->countCS); | ||
| 491 | |||
| 492 | if( ref->waitersCount > 0) | ||
| 493 | { | ||
| 494 | ResetEvent( ref->waitDoneEvent); | ||
| 495 | SetEvent( ref->waitEvent); | ||
| 496 | LeaveCriticalSection( &ref->countCS); | ||
| 497 | errc = WaitForSingleObject( ref->waitDoneEvent, INFINITE); | ||
| 498 | } | ||
| 499 | else | ||
| 500 | { | ||
| 501 | LeaveCriticalSection( &ref->countCS); | ||
| 502 | } | ||
| 503 | |||
| 504 | LeaveCriticalSection( &ref->signalCS); | ||
| 505 | |||
| 506 | if( WAIT_OBJECT_0 != errc) | ||
| 507 | FAIL( "WaitForSingleObject", GetLastError()); | ||
| 508 | } | ||
| 509 | |||
| 510 | #else // CONDITION_VARIABLE are available, use them | ||
| 511 | |||
| 512 | // | ||
| 513 | void SIGNAL_INIT( SIGNAL_T *ref ) | ||
| 514 | { | ||
| 515 | InitializeConditionVariable( ref); | ||
| 516 | } | ||
| 517 | |||
| 518 | void SIGNAL_FREE( SIGNAL_T *ref ) | ||
| 519 | { | ||
| 520 | // nothing to do | ||
| 521 | (void)ref; | ||
| 522 | } | ||
| 523 | |||
| 524 | bool SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs) | ||
| 525 | { | ||
| 526 | long ms; | ||
| 527 | |||
| 528 | if( abs_secs < 0.0) | ||
| 529 | ms = INFINITE; | ||
| 530 | else if( abs_secs == 0.0) | ||
| 531 | ms = 0; | ||
| 532 | else | ||
| 533 | { | ||
| 534 | ms = (long) ((abs_secs - now_secs())*1000.0 + 0.5); | ||
| 535 | |||
| 536 | // If the time already passed, still try once (ms==0). A short timeout | ||
| 537 | // may have turned negative or 0 because of the two time samples done. | ||
| 538 | // | ||
| 539 | if( ms < 0) | ||
| 540 | ms = 0; | ||
| 541 | } | ||
| 542 | |||
| 543 | if( !SleepConditionVariableCS( ref, mu_ref, ms)) | ||
| 544 | { | ||
| 545 | if( GetLastError() == ERROR_TIMEOUT) | ||
| 546 | { | ||
| 547 | return false; | ||
| 548 | } | ||
| 549 | else | ||
| 550 | { | ||
| 551 | FAIL( "SleepConditionVariableCS", GetLastError()); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | return true; | ||
| 555 | } | ||
| 556 | |||
| 557 | void SIGNAL_ONE( SIGNAL_T *ref ) | ||
| 558 | { | ||
| 559 | WakeConditionVariable( ref); | ||
| 560 | } | ||
| 561 | |||
| 562 | void SIGNAL_ALL( SIGNAL_T *ref ) | ||
| 563 | { | ||
| 564 | WakeAllConditionVariable( ref); | ||
| 565 | } | ||
| 566 | |||
| 567 | #endif // CONDITION_VARIABLE are available | ||
| 568 | |||
| 569 | #else // THREADAPI == THREADAPI_PTHREAD | 314 | #else // THREADAPI == THREADAPI_PTHREAD |
| 570 | // PThread (Linux, OS X, ...) | 315 | // PThread (Linux, OS X, ...) |
| 571 | // | 316 | // |
| @@ -607,44 +352,6 @@ bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | |||
| 607 | abort(); | 352 | abort(); |
| 608 | } | 353 | } |
| 609 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } | 354 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } |
| 610 | // | ||
| 611 | void SIGNAL_INIT( SIGNAL_T *ref ) { | ||
| 612 | PT_CALL(pthread_cond_init(ref, nullptr /*attr*/)); | ||
| 613 | } | ||
| 614 | void SIGNAL_FREE( SIGNAL_T *ref ) { | ||
| 615 | PT_CALL( pthread_cond_destroy(ref) ); | ||
| 616 | } | ||
| 617 | // | ||
| 618 | /* | ||
| 619 | * Timeout is given as absolute since we may have fake wakeups during | ||
| 620 | * a timed out sleep. A Linda with some other key read, or just because | ||
| 621 | * PThread cond vars can wake up unwantedly. | ||
| 622 | */ | ||
| 623 | bool SIGNAL_WAIT( SIGNAL_T *ref, pthread_mutex_t *mu, time_d abs_secs ) { | ||
| 624 | if (abs_secs<0.0) { | ||
| 625 | PT_CALL( pthread_cond_wait( ref, mu ) ); // infinite | ||
| 626 | } else { | ||
| 627 | int rc; | ||
| 628 | struct timespec ts; | ||
| 629 | |||
| 630 | assert( abs_secs != 0.0 ); | ||
| 631 | prepare_timeout( &ts, abs_secs ); | ||
| 632 | |||
| 633 | rc= pthread_cond_timedwait( ref, mu, &ts ); | ||
| 634 | |||
| 635 | if (rc==ETIMEDOUT) return false; | ||
| 636 | if (rc) { _PT_FAIL( rc, "pthread_cond_timedwait()", __FILE__, __LINE__ ); } | ||
| 637 | } | ||
| 638 | return true; | ||
| 639 | } | ||
| 640 | // | ||
| 641 | void SIGNAL_ONE( SIGNAL_T *ref ) { | ||
| 642 | PT_CALL( pthread_cond_signal(ref) ); // wake up ONE (or no) waiting thread | ||
| 643 | } | ||
| 644 | // | ||
| 645 | void SIGNAL_ALL( SIGNAL_T *ref ) { | ||
| 646 | PT_CALL( pthread_cond_broadcast(ref) ); // wake up ALL waiting threads | ||
| 647 | } | ||
| 648 | 355 | ||
| 649 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range | 356 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range |
| 650 | static int const gs_prio_remap[] = | 357 | static int const gs_prio_remap[] = |
| @@ -775,129 +482,36 @@ static int select_prio(int prio /* -3..+3 */) | |||
| 775 | return gs_prio_remap[prio + 3]; | 482 | return gs_prio_remap[prio + 3]; |
| 776 | } | 483 | } |
| 777 | 484 | ||
| 778 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) | 485 | void THREAD_SET_PRIORITY( int prio) |
| 779 | { | 486 | { |
| 780 | pthread_attr_t a; | ||
| 781 | bool const change_priority = | ||
| 782 | #ifdef PLATFORM_LINUX | 487 | #ifdef PLATFORM_LINUX |
| 783 | sudo && // only root-privileged process can change priorities | 488 | if( sudo) // only root-privileged process can change priorities |
| 784 | #endif | 489 | #endif // PLATFORM_LINUX |
| 785 | (prio != THREAD_PRIO_DEFAULT); | ||
| 786 | |||
| 787 | PT_CALL( pthread_attr_init( &a)); | ||
| 788 | |||
| 789 | #ifndef PTHREAD_TIMEDJOIN | ||
| 790 | // We create a NON-JOINABLE thread. This is mainly due to the lack of | ||
| 791 | // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier | ||
| 792 | // freeing of the thread's resources). | ||
| 793 | // | ||
| 794 | PT_CALL( pthread_attr_setdetachstate( &a, PTHREAD_CREATE_DETACHED)); | ||
| 795 | #endif // PTHREAD_TIMEDJOIN | ||
| 796 | |||
| 797 | // Use this to find a system's default stack size (DEBUG) | ||
| 798 | #if 0 | ||
| 799 | { | ||
| 800 | size_t n; | ||
| 801 | pthread_attr_getstacksize( &a, &n); | ||
| 802 | fprintf( stderr, "Getstack: %u\n", (unsigned int)n); | ||
| 803 | } | ||
| 804 | // 524288 on OS X | ||
| 805 | // 2097152 on Linux x86 (Ubuntu 7.04) | ||
| 806 | // 1048576 on FreeBSD 6.2 SMP i386 | ||
| 807 | #endif // 0 | ||
| 808 | |||
| 809 | #if defined _THREAD_STACK_SIZE && _THREAD_STACK_SIZE > 0 | ||
| 810 | PT_CALL( pthread_attr_setstacksize( &a, _THREAD_STACK_SIZE)); | ||
| 811 | #endif | ||
| 812 | |||
| 813 | if (change_priority) | ||
| 814 | { | 490 | { |
| 815 | struct sched_param sp; | 491 | struct sched_param sp; |
| 816 | // "The specified scheduling parameters are only used if the scheduling | 492 | // prio range [-3,+3] was checked by the caller |
| 817 | // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED." | 493 | sp.sched_priority = gs_prio_remap[ prio + 3]; |
| 818 | // | 494 | PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); |
| 819 | #if !defined __ANDROID__ || ( defined __ANDROID__ && __ANDROID_API__ >= 28 ) | ||
| 820 | PT_CALL( pthread_attr_setinheritsched( &a, PTHREAD_EXPLICIT_SCHED)); | ||
| 821 | #endif | ||
| 822 | |||
| 823 | #ifdef _PRIO_SCOPE | ||
| 824 | PT_CALL( pthread_attr_setscope( &a, _PRIO_SCOPE)); | ||
| 825 | #endif // _PRIO_SCOPE | ||
| 826 | |||
| 827 | PT_CALL( pthread_attr_setschedpolicy( &a, _PRIO_MODE)); | ||
| 828 | |||
| 829 | sp.sched_priority = select_prio(prio); | ||
| 830 | PT_CALL( pthread_attr_setschedparam( &a, &sp)); | ||
| 831 | } | ||
| 832 | |||
| 833 | //--- | ||
| 834 | // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system | ||
| 835 | // thread limit (not userland thread). Actual limit for us is way higher. | ||
| 836 | // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!) | ||
| 837 | // | ||
| 838 | # ifndef THREAD_CREATE_RETRIES_MAX | ||
| 839 | // Don't bother with retries; a failure is a failure | ||
| 840 | // | ||
| 841 | { | ||
| 842 | int rc = pthread_create( ref, &a, func, data); | ||
| 843 | if( rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ - 1); | ||
| 844 | } | 495 | } |
| 845 | # else | ||
| 846 | # error "This code deprecated" | ||
| 847 | /* | ||
| 848 | // Wait slightly if thread creation has exchausted the system | ||
| 849 | // | ||
| 850 | { int retries; | ||
| 851 | for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) { | ||
| 852 | |||
| 853 | int rc= pthread_create( ref, &a, func, data ); | ||
| 854 | // | ||
| 855 | // OS X / Linux: | ||
| 856 | // EAGAIN: ".. lacked the necessary resources to create | ||
| 857 | // another thread, or the system-imposed limit on the | ||
| 858 | // total number of threads in a process | ||
| 859 | // [PTHREAD_THREADS_MAX] would be exceeded." | ||
| 860 | // EINVAL: attr is invalid | ||
| 861 | // Linux: | ||
| 862 | // EPERM: no rights for given parameters or scheduling (no sudo) | ||
| 863 | // ENOMEM: (known to fail with this code, too - not listed in man) | ||
| 864 | |||
| 865 | if (rc==0) break; // ok! | ||
| 866 | |||
| 867 | // In practise, exhaustion seems to be coming from memory, not a | ||
| 868 | // maximum number of threads. Keep tuning... ;) | ||
| 869 | // | ||
| 870 | if (rc==EAGAIN) { | ||
| 871 | //fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG | ||
| 872 | |||
| 873 | // Try again, later. | ||
| 874 | |||
| 875 | Yield(); | ||
| 876 | } else { | ||
| 877 | _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ ); | ||
| 878 | } | ||
| 879 | } | ||
| 880 | } | ||
| 881 | */ | ||
| 882 | # endif | ||
| 883 | |||
| 884 | PT_CALL( pthread_attr_destroy( &a)); | ||
| 885 | } | 496 | } |
| 886 | 497 | ||
| 498 | // ################################################################################################# | ||
| 887 | 499 | ||
| 888 | void THREAD_SET_PRIORITY( int prio) | 500 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_) |
| 889 | { | 501 | { |
| 890 | #ifdef PLATFORM_LINUX | 502 | #ifdef PLATFORM_LINUX |
| 891 | if( sudo) // only root-privileged process can change priorities | 503 | if (sudo) // only root-privileged process can change priorities |
| 892 | #endif // PLATFORM_LINUX | 504 | #endif // PLATFORM_LINUX |
| 893 | { | 505 | { |
| 894 | struct sched_param sp; | 506 | struct sched_param sp; |
| 895 | // prio range [-3,+3] was checked by the caller | 507 | // prio range [-3,+3] was checked by the caller |
| 896 | sp.sched_priority = gs_prio_remap[ prio + 3]; | 508 | sp.sched_priority = gs_prio_remap[prio_ + 3]; |
| 897 | PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); | 509 | PT_CALL(pthread_setschedparam(static_cast<pthread_t>(thread_.native_handle()), _PRIO_MODE, &sp)); |
| 898 | } | 510 | } |
| 899 | } | 511 | } |
| 900 | 512 | ||
| 513 | // ################################################################################################# | ||
| 514 | |||
| 901 | void THREAD_SET_AFFINITY( unsigned int aff) | 515 | void THREAD_SET_AFFINITY( unsigned int aff) |
| 902 | { | 516 | { |
| 903 | int bit = 0; | 517 | int bit = 0; |
| @@ -929,93 +543,6 @@ void THREAD_SET_AFFINITY( unsigned int aff) | |||
| 929 | #endif | 543 | #endif |
| 930 | } | 544 | } |
| 931 | 545 | ||
| 932 | /* | ||
| 933 | * Wait for a thread to finish. | ||
| 934 | * | ||
| 935 | * 'mu_ref' is a lock we should use for the waiting; initially unlocked. | ||
| 936 | * Same lock as passed to THREAD_EXIT. | ||
| 937 | * | ||
| 938 | * Returns true for successful wait, false for timed out | ||
| 939 | */ | ||
| 940 | bool THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref) | ||
| 941 | { | ||
| 942 | struct timespec ts_store; | ||
| 943 | const struct timespec* timeout = nullptr; | ||
| 944 | bool done; | ||
| 945 | |||
| 946 | // Do timeout counting before the locks | ||
| 947 | // | ||
| 948 | #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT | ||
| 949 | if (secs>=0.0) | ||
| 950 | #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
| 951 | if (secs>0.0) | ||
| 952 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
| 953 | { | ||
| 954 | prepare_timeout( &ts_store, now_secs()+secs ); | ||
| 955 | timeout= &ts_store; | ||
| 956 | } | ||
| 957 | |||
| 958 | #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT | ||
| 959 | /* Thread is joinable | ||
| 960 | */ | ||
| 961 | if (!timeout) { | ||
| 962 | PT_CALL(pthread_join(*ref, nullptr /*ignore exit value*/)); | ||
| 963 | done = true; | ||
| 964 | } else { | ||
| 965 | int rc = PTHREAD_TIMEDJOIN(*ref, nullptr, timeout); | ||
| 966 | if ((rc!=0) && (rc!=ETIMEDOUT)) { | ||
| 967 | _PT_FAIL( rc, "PTHREAD_TIMEDJOIN", __FILE__, __LINE__-2 ); | ||
| 968 | } | ||
| 969 | done= rc==0; | ||
| 970 | } | ||
| 971 | #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
| 972 | /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot | ||
| 973 | * join with it. Use the cond.var. | ||
| 974 | */ | ||
| 975 | (void) ref; // unused | ||
| 976 | MUTEX_LOCK( mu_ref ); | ||
| 977 | |||
| 978 | // 'secs'==0.0 does not need to wait, just take the current status | ||
| 979 | // within the 'mu_ref' locks | ||
| 980 | // | ||
| 981 | if (secs != 0.0) { | ||
| 982 | while( *st_ref < DONE ) { | ||
| 983 | if (!timeout) { | ||
| 984 | PT_CALL( pthread_cond_wait( signal_ref, mu_ref )); | ||
| 985 | } else { | ||
| 986 | int rc= pthread_cond_timedwait( signal_ref, mu_ref, timeout ); | ||
| 987 | if (rc==ETIMEDOUT) break; | ||
| 988 | if (rc!=0) _PT_FAIL( rc, "pthread_cond_timedwait", __FILE__, __LINE__-2 ); | ||
| 989 | } | ||
| 990 | } | ||
| 991 | } | ||
| 992 | done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED | ||
| 993 | |||
| 994 | MUTEX_UNLOCK( mu_ref ); | ||
| 995 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
| 996 | return done; | ||
| 997 | } | ||
| 998 | // | ||
| 999 | void THREAD_KILL( THREAD_T *ref ) { | ||
| 1000 | #ifdef __ANDROID__ | ||
| 1001 | __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot kill thread!"); | ||
| 1002 | #else | ||
| 1003 | pthread_cancel( *ref ); | ||
| 1004 | #endif | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | void THREAD_MAKE_ASYNCH_CANCELLABLE() | ||
| 1008 | { | ||
| 1009 | #ifdef __ANDROID__ | ||
| 1010 | __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot make thread async cancellable!"); | ||
| 1011 | #else | ||
| 1012 | // that's the default, but just in case... | ||
| 1013 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr); | ||
| 1014 | // we want cancellation to take effect immediately if possible, instead of waiting for a cancellation point (which is the default) | ||
| 1015 | pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, nullptr); | ||
| 1016 | #endif | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | void THREAD_SETNAME( char const* _name) | 546 | void THREAD_SETNAME( char const* _name) |
| 1020 | { | 547 | { |
| 1021 | // exact API to set the thread name is platform-dependant | 548 | // exact API to set the thread name is platform-dependant |
