diff options
| author | Eli Zaretskii | 2013-08-31 14:29:05 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2013-08-31 14:29:05 +0300 |
| commit | e57df8f77901a3964d21c3d57fb6769cf4511dc2 (patch) | |
| tree | 1a748293f62f70a786b9f6c51662e1c132650528 /src | |
| parent | dbe17fefccbff010bbbf6c4d0dccc7b2f3a5e201 (diff) | |
| download | emacs-e57df8f77901a3964d21c3d57fb6769cf4511dc2.tar.gz emacs-e57df8f77901a3964d21c3d57fb6769cf4511dc2.zip | |
Improve MS-Windows implementation of threads.
src/systhread.c (sys_cond_init): Set the 'initialized' member to
true only if initialization is successful. Initialize wait_count
and wait_count_lock.
(sys_cond_wait, sys_cond_signal, sys_cond_broadcast): If
'initialized' is false, do nothing.
(sys_cond_wait): Fix the implementation to avoid the "missed
wakeup" bug: count the waiting threads, and reset the broadcast
event once the last thread was released.
(sys_cond_signal, sys_cond_broadcast): Use SetEvent instead of
PulseEvent. Don't signal the event if no threads are waiting.
(sys_cond_destroy): Only close non-NULL handles.
(sys_thread_create): Return zero if unsuccessful, 1 if successful.
src/systhread.h (w32thread_cond_t): New member 'initialized'.
Rename waiters_count and waiters_count_lock to wait_count and
wait_count_lock, respectively.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 19 | ||||
| -rw-r--r-- | src/process.c | 3 | ||||
| -rw-r--r-- | src/systhread.c | 82 | ||||
| -rw-r--r-- | src/systhread.h | 8 |
4 files changed, 98 insertions, 14 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 0aef16d34e3..3e901d84db9 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,22 @@ | |||
| 1 | 2013-08-31 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * systhread.c (sys_cond_init): Set the 'initialized' member to | ||
| 4 | true only if initialization is successful. Initialize wait_count | ||
| 5 | and wait_count_lock. | ||
| 6 | (sys_cond_wait, sys_cond_signal, sys_cond_broadcast): If | ||
| 7 | 'initialized' is false, do nothing. | ||
| 8 | (sys_cond_wait): Fix the implementation to avoid the "missed | ||
| 9 | wakeup" bug: count the waiting threads, and reset the broadcast | ||
| 10 | event once the last thread was released. | ||
| 11 | (sys_cond_signal, sys_cond_broadcast): Use SetEvent instead of | ||
| 12 | PulseEvent. Don't signal the event if no threads are waiting. | ||
| 13 | (sys_cond_destroy): Only close non-NULL handles. | ||
| 14 | (sys_thread_create): Return zero if unsuccessful, 1 if successful. | ||
| 15 | |||
| 16 | * systhread.h (w32thread_cond_t): New member 'initialized'. | ||
| 17 | Rename waiters_count and waiters_count_lock to wait_count and | ||
| 18 | wait_count_lock, respectively. | ||
| 19 | |||
| 1 | 2013-08-30 Eli Zaretskii <eliz@gnu.org> | 20 | 2013-08-30 Eli Zaretskii <eliz@gnu.org> |
| 2 | 21 | ||
| 3 | * systhread.h (w32thread_critsect, w32thread_cond_t, sys_mutex_t) | 22 | * systhread.h (w32thread_critsect, w32thread_cond_t, sys_mutex_t) |
diff --git a/src/process.c b/src/process.c index 94ca3d4b1a0..899c0035866 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -497,7 +497,6 @@ void | |||
| 497 | delete_read_fd (int fd) | 497 | delete_read_fd (int fd) |
| 498 | { | 498 | { |
| 499 | eassert (fd < MAXDESC); | 499 | eassert (fd < MAXDESC); |
| 500 | eassert (fd <= max_desc); | ||
| 501 | delete_keyboard_wait_descriptor (fd); | 500 | delete_keyboard_wait_descriptor (fd); |
| 502 | 501 | ||
| 503 | if (fd_callback_info[fd].flags == 0) | 502 | if (fd_callback_info[fd].flags == 0) |
| @@ -559,7 +558,6 @@ delete_write_fd (int fd) | |||
| 559 | int lim = max_desc; | 558 | int lim = max_desc; |
| 560 | 559 | ||
| 561 | eassert (fd < MAXDESC); | 560 | eassert (fd < MAXDESC); |
| 562 | eassert (fd <= max_desc); | ||
| 563 | 561 | ||
| 564 | #ifdef NON_BLOCKING_CONNECT | 562 | #ifdef NON_BLOCKING_CONNECT |
| 565 | if ((fd_callback_info[fd].flags & NON_BLOCKING_CONNECT_FD) != 0) | 563 | if ((fd_callback_info[fd].flags & NON_BLOCKING_CONNECT_FD) != 0) |
| @@ -6942,7 +6940,6 @@ delete_keyboard_wait_descriptor (int desc) | |||
| 6942 | int lim = max_desc; | 6940 | int lim = max_desc; |
| 6943 | 6941 | ||
| 6944 | eassert (desc >= 0 && desc < MAXDESC); | 6942 | eassert (desc >= 0 && desc < MAXDESC); |
| 6945 | eassert (desc <= max_desc); | ||
| 6946 | 6943 | ||
| 6947 | fd_callback_info[desc].flags &= ~(FOR_READ | KEYBOARD_FD | PROCESS_FD); | 6944 | fd_callback_info[desc].flags &= ~(FOR_READ | KEYBOARD_FD | PROCESS_FD); |
| 6948 | 6945 | ||
diff --git a/src/systhread.c b/src/systhread.c index b154abaecd6..bde0f027e78 100644 --- a/src/systhread.c +++ b/src/systhread.c | |||
| @@ -243,37 +243,101 @@ sys_mutex_destroy (sys_mutex_t *mutex) | |||
| 243 | void | 243 | void |
| 244 | sys_cond_init (sys_cond_t *cond) | 244 | sys_cond_init (sys_cond_t *cond) |
| 245 | { | 245 | { |
| 246 | cond->initialized = false; | ||
| 247 | cond->wait_count = 0; | ||
| 248 | /* Auto-reset event for signal. */ | ||
| 246 | cond->events[CONDV_SIGNAL] = CreateEvent (NULL, FALSE, FALSE, NULL); | 249 | cond->events[CONDV_SIGNAL] = CreateEvent (NULL, FALSE, FALSE, NULL); |
| 250 | /* Manual-reset event for broadcast. */ | ||
| 247 | cond->events[CONDV_BROADCAST] = CreateEvent (NULL, TRUE, FALSE, NULL); | 251 | cond->events[CONDV_BROADCAST] = CreateEvent (NULL, TRUE, FALSE, NULL); |
| 252 | if (!cond->events[CONDV_SIGNAL] || !cond->events[CONDV_BROADCAST]) | ||
| 253 | return; | ||
| 254 | InitializeCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 255 | cond->initialized = true; | ||
| 248 | } | 256 | } |
| 249 | 257 | ||
| 250 | void | 258 | void |
| 251 | sys_cond_wait (sys_cond_t *cond, sys_mutex_t *mutex) | 259 | sys_cond_wait (sys_cond_t *cond, sys_mutex_t *mutex) |
| 252 | { | 260 | { |
| 253 | /* FIXME: This implementation is simple, but incorrect. Stay tuned | 261 | DWORD wait_result; |
| 254 | for better and more complicated implementation. */ | 262 | bool last_thread_waiting; |
| 263 | |||
| 264 | if (!cond->initialized) | ||
| 265 | return; | ||
| 266 | |||
| 267 | /* Increment the wait count avoiding race conditions. */ | ||
| 268 | EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 269 | cond->wait_count++; | ||
| 270 | LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 271 | |||
| 272 | /* Release the mutex and wait for either the signal or the broadcast | ||
| 273 | event. */ | ||
| 255 | LeaveCriticalSection ((LPCRITICAL_SECTION)mutex); | 274 | LeaveCriticalSection ((LPCRITICAL_SECTION)mutex); |
| 256 | WaitForMultipleObjects (2, cond->events, FALSE, INFINITE); | 275 | wait_result = WaitForMultipleObjects (2, cond->events, FALSE, INFINITE); |
| 276 | |||
| 277 | /* Decrement the wait count and see if we are the last thread | ||
| 278 | waiting on the condition variable. */ | ||
| 279 | EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 280 | cond->wait_count--; | ||
| 281 | last_thread_waiting = | ||
| 282 | wait_result == WAIT_OBJECT_0 + CONDV_BROADCAST | ||
| 283 | && cond->wait_count == 0; | ||
| 284 | LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 285 | |||
| 286 | /* Broadcast uses a manual-reset event, so when the last thread is | ||
| 287 | released, we must manually reset that event. */ | ||
| 288 | if (last_thread_waiting) | ||
| 289 | ResetEvent (cond->events[CONDV_BROADCAST]); | ||
| 290 | |||
| 291 | /* Per the API, re-acquire the mutex. */ | ||
| 257 | EnterCriticalSection ((LPCRITICAL_SECTION)mutex); | 292 | EnterCriticalSection ((LPCRITICAL_SECTION)mutex); |
| 258 | } | 293 | } |
| 259 | 294 | ||
| 260 | void | 295 | void |
| 261 | sys_cond_signal (sys_cond_t *cond) | 296 | sys_cond_signal (sys_cond_t *cond) |
| 262 | { | 297 | { |
| 263 | PulseEvent (cond->events[CONDV_SIGNAL]); | 298 | bool threads_waiting; |
| 299 | |||
| 300 | if (!cond->initialized) | ||
| 301 | return; | ||
| 302 | |||
| 303 | EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 304 | threads_waiting = cond->wait_count > 0; | ||
| 305 | LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 306 | |||
| 307 | if (threads_waiting) | ||
| 308 | SetEvent (cond->events[CONDV_SIGNAL]); | ||
| 264 | } | 309 | } |
| 265 | 310 | ||
| 266 | void | 311 | void |
| 267 | sys_cond_broadcast (sys_cond_t *cond) | 312 | sys_cond_broadcast (sys_cond_t *cond) |
| 268 | { | 313 | { |
| 269 | PulseEvent (cond->events[CONDV_BROADCAST]); | 314 | bool threads_waiting; |
| 315 | |||
| 316 | if (!cond->initialized) | ||
| 317 | return; | ||
| 318 | |||
| 319 | EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 320 | threads_waiting = cond->wait_count > 0; | ||
| 321 | LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 322 | |||
| 323 | if (threads_waiting) | ||
| 324 | SetEvent (cond->events[CONDV_BROADCAST]); | ||
| 270 | } | 325 | } |
| 271 | 326 | ||
| 272 | void | 327 | void |
| 273 | sys_cond_destroy (sys_cond_t *cond) | 328 | sys_cond_destroy (sys_cond_t *cond) |
| 274 | { | 329 | { |
| 275 | CloseHandle (cond->events[CONDV_SIGNAL]); | 330 | if (cond->events[CONDV_SIGNAL]) |
| 276 | CloseHandle (cond->events[CONDV_BROADCAST]); | 331 | CloseHandle (cond->events[CONDV_SIGNAL]); |
| 332 | if (cond->events[CONDV_BROADCAST]) | ||
| 333 | CloseHandle (cond->events[CONDV_BROADCAST]); | ||
| 334 | |||
| 335 | if (!cond->initialized) | ||
| 336 | return; | ||
| 337 | |||
| 338 | /* FIXME: What if wait_count is non-zero, i.e. there are still | ||
| 339 | threads waiting on this condition variable? */ | ||
| 340 | DeleteCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock); | ||
| 277 | } | 341 | } |
| 278 | 342 | ||
| 279 | sys_thread_t | 343 | sys_thread_t |
| @@ -322,7 +386,7 @@ sys_thread_create (sys_thread_t *thread_ptr, const char *name, | |||
| 322 | rule in many places... */ | 386 | rule in many places... */ |
| 323 | thandle = _beginthread (w32_beginthread_wrapper, stack_size, arg); | 387 | thandle = _beginthread (w32_beginthread_wrapper, stack_size, arg); |
| 324 | if (thandle == (uintptr_t)-1L) | 388 | if (thandle == (uintptr_t)-1L) |
| 325 | return errno; | 389 | return 0; |
| 326 | 390 | ||
| 327 | /* Kludge alert! We use the Windows thread ID, an unsigned 32-bit | 391 | /* Kludge alert! We use the Windows thread ID, an unsigned 32-bit |
| 328 | number, as the sys_thread_t type, because that ID is the only | 392 | number, as the sys_thread_t type, because that ID is the only |
| @@ -337,7 +401,7 @@ sys_thread_create (sys_thread_t *thread_ptr, const char *name, | |||
| 337 | Therefore, we return some more or less arbitrary value of the | 401 | Therefore, we return some more or less arbitrary value of the |
| 338 | thread ID from this function. */ | 402 | thread ID from this function. */ |
| 339 | *thread_ptr = thandle & 0xFFFFFFFF; | 403 | *thread_ptr = thandle & 0xFFFFFFFF; |
| 340 | return 0; | 404 | return 1; |
| 341 | } | 405 | } |
| 342 | 406 | ||
| 343 | void | 407 | void |
diff --git a/src/systhread.h b/src/systhread.h index 52735449a5e..b38fd8ffd45 100644 --- a/src/systhread.h +++ b/src/systhread.h | |||
| @@ -56,9 +56,13 @@ typedef struct { | |||
| 56 | enum { CONDV_SIGNAL = 0, CONDV_BROADCAST = 1, CONDV_MAX = 2 }; | 56 | enum { CONDV_SIGNAL = 0, CONDV_BROADCAST = 1, CONDV_MAX = 2 }; |
| 57 | 57 | ||
| 58 | typedef struct { | 58 | typedef struct { |
| 59 | unsigned waiters_count; | 59 | /* Count of threads that are waiting for this condition variable. */ |
| 60 | w32thread_critsect waiters_count_lock; | 60 | unsigned wait_count; |
| 61 | /* Critical section to protect changes to the count above. */ | ||
| 62 | w32thread_critsect wait_count_lock; | ||
| 63 | /* Handles of events used for signal and broadcast. */ | ||
| 61 | void *events[CONDV_MAX]; | 64 | void *events[CONDV_MAX]; |
| 65 | bool initialized; | ||
| 62 | } w32thread_cond_t; | 66 | } w32thread_cond_t; |
| 63 | 67 | ||
| 64 | typedef w32thread_critsect sys_mutex_t; | 68 | typedef w32thread_critsect sys_mutex_t; |