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.
This commit is contained in:
parent
dbe17fefcc
commit
e57df8f779
4 changed files with 98 additions and 14 deletions
|
@ -1,3 +1,22 @@
|
|||
2013-08-31 Eli Zaretskii <eliz@gnu.org>
|
||||
|
||||
* 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.
|
||||
|
||||
* systhread.h (w32thread_cond_t): New member 'initialized'.
|
||||
Rename waiters_count and waiters_count_lock to wait_count and
|
||||
wait_count_lock, respectively.
|
||||
|
||||
2013-08-30 Eli Zaretskii <eliz@gnu.org>
|
||||
|
||||
* systhread.h (w32thread_critsect, w32thread_cond_t, sys_mutex_t)
|
||||
|
|
|
@ -497,7 +497,6 @@ void
|
|||
delete_read_fd (int fd)
|
||||
{
|
||||
eassert (fd < MAXDESC);
|
||||
eassert (fd <= max_desc);
|
||||
delete_keyboard_wait_descriptor (fd);
|
||||
|
||||
if (fd_callback_info[fd].flags == 0)
|
||||
|
@ -559,7 +558,6 @@ delete_write_fd (int fd)
|
|||
int lim = max_desc;
|
||||
|
||||
eassert (fd < MAXDESC);
|
||||
eassert (fd <= max_desc);
|
||||
|
||||
#ifdef NON_BLOCKING_CONNECT
|
||||
if ((fd_callback_info[fd].flags & NON_BLOCKING_CONNECT_FD) != 0)
|
||||
|
@ -6942,7 +6940,6 @@ delete_keyboard_wait_descriptor (int desc)
|
|||
int lim = max_desc;
|
||||
|
||||
eassert (desc >= 0 && desc < MAXDESC);
|
||||
eassert (desc <= max_desc);
|
||||
|
||||
fd_callback_info[desc].flags &= ~(FOR_READ | KEYBOARD_FD | PROCESS_FD);
|
||||
|
||||
|
|
|
@ -243,37 +243,101 @@ sys_mutex_destroy (sys_mutex_t *mutex)
|
|||
void
|
||||
sys_cond_init (sys_cond_t *cond)
|
||||
{
|
||||
cond->initialized = false;
|
||||
cond->wait_count = 0;
|
||||
/* Auto-reset event for signal. */
|
||||
cond->events[CONDV_SIGNAL] = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||
/* Manual-reset event for broadcast. */
|
||||
cond->events[CONDV_BROADCAST] = CreateEvent (NULL, TRUE, FALSE, NULL);
|
||||
if (!cond->events[CONDV_SIGNAL] || !cond->events[CONDV_BROADCAST])
|
||||
return;
|
||||
InitializeCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
cond->initialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
sys_cond_wait (sys_cond_t *cond, sys_mutex_t *mutex)
|
||||
{
|
||||
/* FIXME: This implementation is simple, but incorrect. Stay tuned
|
||||
for better and more complicated implementation. */
|
||||
DWORD wait_result;
|
||||
bool last_thread_waiting;
|
||||
|
||||
if (!cond->initialized)
|
||||
return;
|
||||
|
||||
/* Increment the wait count avoiding race conditions. */
|
||||
EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
cond->wait_count++;
|
||||
LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
|
||||
/* Release the mutex and wait for either the signal or the broadcast
|
||||
event. */
|
||||
LeaveCriticalSection ((LPCRITICAL_SECTION)mutex);
|
||||
WaitForMultipleObjects (2, cond->events, FALSE, INFINITE);
|
||||
wait_result = WaitForMultipleObjects (2, cond->events, FALSE, INFINITE);
|
||||
|
||||
/* Decrement the wait count and see if we are the last thread
|
||||
waiting on the condition variable. */
|
||||
EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
cond->wait_count--;
|
||||
last_thread_waiting =
|
||||
wait_result == WAIT_OBJECT_0 + CONDV_BROADCAST
|
||||
&& cond->wait_count == 0;
|
||||
LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
|
||||
/* Broadcast uses a manual-reset event, so when the last thread is
|
||||
released, we must manually reset that event. */
|
||||
if (last_thread_waiting)
|
||||
ResetEvent (cond->events[CONDV_BROADCAST]);
|
||||
|
||||
/* Per the API, re-acquire the mutex. */
|
||||
EnterCriticalSection ((LPCRITICAL_SECTION)mutex);
|
||||
}
|
||||
|
||||
void
|
||||
sys_cond_signal (sys_cond_t *cond)
|
||||
{
|
||||
PulseEvent (cond->events[CONDV_SIGNAL]);
|
||||
bool threads_waiting;
|
||||
|
||||
if (!cond->initialized)
|
||||
return;
|
||||
|
||||
EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
threads_waiting = cond->wait_count > 0;
|
||||
LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
|
||||
if (threads_waiting)
|
||||
SetEvent (cond->events[CONDV_SIGNAL]);
|
||||
}
|
||||
|
||||
void
|
||||
sys_cond_broadcast (sys_cond_t *cond)
|
||||
{
|
||||
PulseEvent (cond->events[CONDV_BROADCAST]);
|
||||
bool threads_waiting;
|
||||
|
||||
if (!cond->initialized)
|
||||
return;
|
||||
|
||||
EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
threads_waiting = cond->wait_count > 0;
|
||||
LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
|
||||
if (threads_waiting)
|
||||
SetEvent (cond->events[CONDV_BROADCAST]);
|
||||
}
|
||||
|
||||
void
|
||||
sys_cond_destroy (sys_cond_t *cond)
|
||||
{
|
||||
CloseHandle (cond->events[CONDV_SIGNAL]);
|
||||
CloseHandle (cond->events[CONDV_BROADCAST]);
|
||||
if (cond->events[CONDV_SIGNAL])
|
||||
CloseHandle (cond->events[CONDV_SIGNAL]);
|
||||
if (cond->events[CONDV_BROADCAST])
|
||||
CloseHandle (cond->events[CONDV_BROADCAST]);
|
||||
|
||||
if (!cond->initialized)
|
||||
return;
|
||||
|
||||
/* FIXME: What if wait_count is non-zero, i.e. there are still
|
||||
threads waiting on this condition variable? */
|
||||
DeleteCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
|
||||
}
|
||||
|
||||
sys_thread_t
|
||||
|
@ -322,7 +386,7 @@ sys_thread_create (sys_thread_t *thread_ptr, const char *name,
|
|||
rule in many places... */
|
||||
thandle = _beginthread (w32_beginthread_wrapper, stack_size, arg);
|
||||
if (thandle == (uintptr_t)-1L)
|
||||
return errno;
|
||||
return 0;
|
||||
|
||||
/* Kludge alert! We use the Windows thread ID, an unsigned 32-bit
|
||||
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,
|
|||
Therefore, we return some more or less arbitrary value of the
|
||||
thread ID from this function. */
|
||||
*thread_ptr = thandle & 0xFFFFFFFF;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -56,9 +56,13 @@ typedef struct {
|
|||
enum { CONDV_SIGNAL = 0, CONDV_BROADCAST = 1, CONDV_MAX = 2 };
|
||||
|
||||
typedef struct {
|
||||
unsigned waiters_count;
|
||||
w32thread_critsect waiters_count_lock;
|
||||
/* Count of threads that are waiting for this condition variable. */
|
||||
unsigned wait_count;
|
||||
/* Critical section to protect changes to the count above. */
|
||||
w32thread_critsect wait_count_lock;
|
||||
/* Handles of events used for signal and broadcast. */
|
||||
void *events[CONDV_MAX];
|
||||
bool initialized;
|
||||
} w32thread_cond_t;
|
||||
|
||||
typedef w32thread_critsect sys_mutex_t;
|
||||
|
|
Loading…
Add table
Reference in a new issue