diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/threading.c | 155 | ||||
| -rw-r--r-- | src/threading.h | 12 |
2 files changed, 97 insertions, 70 deletions
diff --git a/src/threading.c b/src/threading.c index 43e5f46..1e4fc92 100644 --- a/src/threading.c +++ b/src/threading.c | |||
| @@ -358,79 +358,98 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | |||
| 358 | #endif // !__GNUC__ | 358 | #endif // !__GNUC__ |
| 359 | } | 359 | } |
| 360 | 360 | ||
| 361 | #if WINVER <= 0x0400 // Windows NT4: Use PulseEvent, although it is unreliable, but then... | 361 | #if WINVER <= 0x0400 // Windows NT4 |
| 362 | 362 | ||
| 363 | // | 363 | void SIGNAL_INIT( SIGNAL_T* ref) |
| 364 | void SIGNAL_INIT( SIGNAL_T *ref ) { | 364 | { |
| 365 | // 'manual reset' event type selected, to be able to wake up all the | 365 | InitializeCriticalSection( &ref->signalCS); |
| 366 | // waiting threads. | 366 | InitializeCriticalSection( &ref->countCS); |
| 367 | // | 367 | if( 0 == (ref->waitEvent = CreateEvent( 0, TRUE, FALSE, 0))) // manual-reset |
| 368 | HANDLE h= CreateEvent( NULL, // security attributes | 368 | FAIL( "CreateEvent", GetLastError()); |
| 369 | TRUE, // TRUE: manual event | 369 | if( 0 == (ref->waitDoneEvent = CreateEvent( 0, FALSE, FALSE, 0))) // auto-reset |
| 370 | FALSE, // Initial state | 370 | FAIL( "CreateEvent", GetLastError()); |
| 371 | NULL ); // name | 371 | ref->waitersCount = 0; |
| 372 | } | ||
| 372 | 373 | ||
| 373 | if (h == NULL) FAIL( "CreateEvent", GetLastError() ); | 374 | void SIGNAL_FREE( SIGNAL_T* ref) |
| 374 | *ref= h; | 375 | { |
| 375 | } | 376 | CloseHandle( ref->waitDoneEvent); |
| 376 | void SIGNAL_FREE( SIGNAL_T *ref ) { | 377 | CloseHandle( ref->waitEvent); |
| 377 | if (!CloseHandle(*ref)) FAIL( "CloseHandle (event)", GetLastError() ); | 378 | DeleteCriticalSection( &ref->countCS); |
| 378 | *ref= NULL; | 379 | DeleteCriticalSection( &ref->signalCS); |
| 379 | } | 380 | } |
| 380 | // | ||
| 381 | bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs ) { | ||
| 382 | DWORD rc; | ||
| 383 | long ms; | ||
| 384 | |||
| 385 | if (abs_secs<0.0) | ||
| 386 | ms= INFINITE; | ||
| 387 | else if (abs_secs==0.0) | ||
| 388 | ms= 0; | ||
| 389 | else { | ||
| 390 | ms= (long) ((abs_secs - now_secs())*1000.0 + 0.5); | ||
| 391 | |||
| 392 | // If the time already passed, still try once (ms==0). A short timeout | ||
| 393 | // may have turned negative or 0 because of the two time samples done. | ||
| 394 | // | ||
| 395 | if (ms<0) ms= 0; | ||
| 396 | } | ||
| 397 | 381 | ||
| 398 | // Unlock and start a wait, atomically (like condition variables do) | 382 | bool_t SIGNAL_WAIT( SIGNAL_T* ref, MUTEX_T* mu_ref, time_d abs_secs) |
| 399 | // | 383 | { |
| 400 | rc= SignalObjectAndWait( *mu_ref, // "object to signal" (unlock) | 384 | DWORD errc; |
| 401 | *ref, // "object to wait on" | 385 | DWORD ms; |
| 402 | ms, | ||
| 403 | FALSE ); // not alertable | ||
| 404 | 386 | ||
| 405 | // All waiting locks are woken here; each competes for the lock in turn. | 387 | if( abs_secs < 0.0) |
| 406 | // | 388 | ms = INFINITE; |
| 407 | // Note: We must get the lock even if we've timed out; it makes upper | 389 | else if( abs_secs == 0.0) |
| 408 | // level code equivalent to how PThread does it. | 390 | ms = 0; |
| 409 | // | 391 | else |
| 410 | MUTEX_LOCK(mu_ref); | 392 | { |
| 393 | time_d msd = (abs_secs - now_secs()) * 1000.0 + 0.5; | ||
| 394 | // If the time already passed, still try once (ms==0). A short timeout | ||
| 395 | // may have turned negative or 0 because of the two time samples done. | ||
| 396 | ms = msd <= 0.0 ? 0 : (DWORD)msd; | ||
| 397 | } | ||
| 411 | 398 | ||
| 412 | if (rc==WAIT_TIMEOUT) return FALSE; | 399 | EnterCriticalSection( &ref->signalCS); |
| 413 | if (rc!=0) FAIL( "SignalObjectAndWait", rc ); | 400 | EnterCriticalSection( &ref->countCS); |
| 414 | return TRUE; | 401 | ++ ref->waitersCount; |
| 415 | } | 402 | LeaveCriticalSection( &ref->countCS); |
| 416 | void SIGNAL_ALL( SIGNAL_T *ref ) { | 403 | LeaveCriticalSection( &ref->signalCS); |
| 417 | /* | 404 | |
| 418 | * MSDN tries to scare that 'PulseEvent' is bad, unreliable and should not be | 405 | errc = SignalObjectAndWait( *mu_ref, ref->waitEvent, ms, FALSE); |
| 419 | * used. Use condition variables instead (wow, they have that!?!); which will | 406 | |
| 420 | * ONLY WORK on Vista and 2008 Server, it seems... so MS, isn't it. | 407 | EnterCriticalSection( &ref->countCS); |
| 421 | * | 408 | if( 0 == -- ref->waitersCount) |
| 422 | * I refuse to believe that; using 'PulseEvent' is probably just as good as | 409 | { |
| 423 | * using Windows (XP) in the first place. Just don't use APC's (asynchronous | 410 | // we're the last one leaving... |
| 424 | * process calls) in your C side coding. | 411 | ResetEvent( ref->waitEvent); |
| 425 | */ | 412 | SetEvent( ref->waitDoneEvent); |
| 426 | // PulseEvent on manual event: | 413 | } |
| 427 | // | 414 | LeaveCriticalSection( &ref->countCS); |
| 428 | // Release ALL threads waiting for it (and go instantly back to unsignalled | 415 | MUTEX_LOCK( mu_ref); |
| 429 | // status = future threads to start a wait will wait) | 416 | |
| 430 | // | 417 | switch( errc) |
| 431 | if (!PulseEvent( *ref )) | 418 | { |
| 432 | FAIL( "PulseEvent", GetLastError() ); | 419 | case WAIT_TIMEOUT: |
| 433 | } | 420 | return FALSE; |
| 421 | case WAIT_OBJECT_0: | ||
| 422 | return TRUE; | ||
| 423 | } | ||
| 424 | |||
| 425 | FAIL( "SignalObjectAndWait", GetLastError()); | ||
| 426 | return FALSE; | ||
| 427 | } | ||
| 428 | |||
| 429 | void SIGNAL_ALL( SIGNAL_T* ref) | ||
| 430 | { | ||
| 431 | DWORD errc = WAIT_OBJECT_0; | ||
| 432 | |||
| 433 | EnterCriticalSection( &ref->signalCS); | ||
| 434 | EnterCriticalSection( &ref->countCS); | ||
| 435 | |||
| 436 | if( ref->waitersCount > 0) | ||
| 437 | { | ||
| 438 | ResetEvent( ref->waitDoneEvent); | ||
| 439 | SetEvent( ref->waitEvent); | ||
| 440 | LeaveCriticalSection( &ref->countCS); | ||
| 441 | errc = WaitForSingleObject( ref->waitDoneEvent, INFINITE); | ||
| 442 | } | ||
| 443 | else | ||
| 444 | { | ||
| 445 | LeaveCriticalSection( &ref->countCS); | ||
| 446 | } | ||
| 447 | |||
| 448 | LeaveCriticalSection( &ref->signalCS); | ||
| 449 | |||
| 450 | if( WAIT_OBJECT_0 != errc) | ||
| 451 | FAIL( "WaitForSingleObject", GetLastError()); | ||
| 452 | } | ||
| 434 | 453 | ||
| 435 | #else // Windows Vista and above: condition variables exist, use them | 454 | #else // Windows Vista and above: condition variables exist, use them |
| 436 | 455 | ||
diff --git a/src/threading.h b/src/threading.h index e559910..7d94f26 100644 --- a/src/threading.h +++ b/src/threading.h | |||
| @@ -64,7 +64,7 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | |||
| 64 | #else // !PLATFORM_XBOX | 64 | #else // !PLATFORM_XBOX |
| 65 | #define WIN32_LEAN_AND_MEAN | 65 | #define WIN32_LEAN_AND_MEAN |
| 66 | // 'SignalObjectAndWait' needs this (targets Windows 2000 and above) | 66 | // 'SignalObjectAndWait' needs this (targets Windows 2000 and above) |
| 67 | //#define _WIN32_WINNT 0x0500 Let the compiler decide depending on the host OS | 67 | #define _WIN32_WINNT 0x0400 |
| 68 | #include <windows.h> | 68 | #include <windows.h> |
| 69 | #endif // !PLATFORM_XBOX | 69 | #endif // !PLATFORM_XBOX |
| 70 | #include <process.h> | 70 | #include <process.h> |
| @@ -76,8 +76,16 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | |||
| 76 | // | 76 | // |
| 77 | 77 | ||
| 78 | #if WINVER <= 0x0400 // Windows NT4: use a signal | 78 | #if WINVER <= 0x0400 // Windows NT4: use a signal |
| 79 | typedef struct | ||
| 80 | { | ||
| 81 | CRITICAL_SECTION signalCS; | ||
| 82 | CRITICAL_SECTION countCS; | ||
| 83 | HANDLE waitEvent; | ||
| 84 | HANDLE waitDoneEvent; | ||
| 85 | LONG waitersCount; | ||
| 86 | } SIGNAL_T; | ||
| 87 | |||
| 79 | 88 | ||
| 80 | #define SIGNAL_T HANDLE | ||
| 81 | #define MUTEX_T HANDLE | 89 | #define MUTEX_T HANDLE |
| 82 | void MUTEX_INIT( MUTEX_T* ref); | 90 | void MUTEX_INIT( MUTEX_T* ref); |
| 83 | void MUTEX_FREE( MUTEX_T* ref); | 91 | void MUTEX_FREE( MUTEX_T* ref); |
