* bsd_mutex.cc: Include limits.h.
(MSLEEP_MUTEX): New define for third parameter to msleep_event_name. (MSLEEP_SEM): Ditto. (MSLEEP_EVENT): Ditto. (msleep_event_name): Add third parameter to allow multiple synchronization objects per ident. (_msleep): Implement new synchronization technique to make sure that all threads have been woken up by a corresponding wakeup call. (wakeup): Ditto.
This commit is contained in:
parent
3a39329a25
commit
f312634c02
@ -1,3 +1,15 @@
|
|||||||
|
2004-12-30 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* bsd_mutex.cc: Include limits.h.
|
||||||
|
(MSLEEP_MUTEX): New define for third parameter to msleep_event_name.
|
||||||
|
(MSLEEP_SEM): Ditto.
|
||||||
|
(MSLEEP_EVENT): Ditto.
|
||||||
|
(msleep_event_name): Add third parameter to allow multiple
|
||||||
|
synchronization objects per ident.
|
||||||
|
(_msleep): Implement new synchronization technique to make sure
|
||||||
|
that all threads have been woken up by a corresponding wakeup call.
|
||||||
|
(wakeup): Ditto.
|
||||||
|
|
||||||
2004-10-18 Corinna Vinschen <corinna@vinschen.de>
|
2004-10-18 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* sysv_sem.cc: Redefine offsetof to circumvent build problems with
|
* sysv_sem.cc: Redefine offsetof to circumvent build problems with
|
||||||
|
@ -13,6 +13,7 @@ details. */
|
|||||||
#define _KERNEL 1
|
#define _KERNEL 1
|
||||||
#define __BSD_VISIBLE 1
|
#define __BSD_VISIBLE 1
|
||||||
#include <sys/smallprint.h>
|
#include <sys/smallprint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "cygserver_ipc.h"
|
#include "cygserver_ipc.h"
|
||||||
@ -96,13 +97,20 @@ mtx_destroy (mtx *m)
|
|||||||
/*
|
/*
|
||||||
* Helper functions for msleep/wakeup.
|
* Helper functions for msleep/wakeup.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Values for which */
|
||||||
|
#define MSLEEP_MUTEX 0
|
||||||
|
#define MSLEEP_SEM 1
|
||||||
|
#define MSLEEP_EVENT 2
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
msleep_event_name (void *ident, char *name)
|
msleep_event_name (void *ident, char *name, int which)
|
||||||
{
|
{
|
||||||
if (wincap.has_terminal_services ())
|
if (wincap.has_terminal_services ())
|
||||||
__small_sprintf (name, "Global\\cygserver.msleep.evt.%08x", ident);
|
__small_sprintf (name, "Global\\cygserver.msleep.evt.%1d.%08x",
|
||||||
|
which, ident);
|
||||||
else
|
else
|
||||||
__small_sprintf (name, "cygserver.msleep.evt.%08x", ident);
|
__small_sprintf (name, "cygserver.msleep.evt.%1d.%08x", which, ident);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,8 +187,37 @@ _msleep (void *ident, struct mtx *mtx, int priority,
|
|||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
char name[64];
|
char name[64];
|
||||||
msleep_event_name (ident, name);
|
|
||||||
HANDLE evt = CreateEvent (NULL, TRUE, FALSE, name);
|
/* The mutex is used to indicate an ident specific critical section.
|
||||||
|
The critical section is needed to synchronize access to the
|
||||||
|
semaphore and eventually the event object. The whole idea is
|
||||||
|
that a wakeup is *guaranteed* to wakeup *all* threads. If that's
|
||||||
|
not synchronized, sleeping threads could return into the msleep
|
||||||
|
function before all other threads have called CloseHandle(evt).
|
||||||
|
That's bad, since the event still exists and is signalled! */
|
||||||
|
HANDLE mutex = CreateMutex (NULL, FALSE,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_MUTEX));
|
||||||
|
if (!mutex)
|
||||||
|
panic ("CreateMutex in msleep (%s) failed: %E", wmesg);
|
||||||
|
WaitForSingleObject (mutex, INFINITE);
|
||||||
|
|
||||||
|
/* Ok, we're in the critical section now. We create an ident specific
|
||||||
|
semaphore, which is used to synchronize the waiting threads. */
|
||||||
|
HANDLE sem = CreateSemaphore (NULL, 0, LONG_MAX,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_SEM));
|
||||||
|
if (!sem)
|
||||||
|
panic ("CreateSemaphore in msleep (%s) failed: %E", wmesg);
|
||||||
|
|
||||||
|
/* This thread is one more thread sleeping. The semaphore value is
|
||||||
|
so used as a counter of sleeping threads. That info is needed by
|
||||||
|
the wakeup function. */
|
||||||
|
ReleaseSemaphore (sem, 1, NULL);
|
||||||
|
|
||||||
|
/* Leave critical section. */
|
||||||
|
ReleaseMutex (mutex);
|
||||||
|
|
||||||
|
HANDLE evt = CreateEvent (NULL, TRUE, FALSE,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_EVENT));
|
||||||
if (!evt)
|
if (!evt)
|
||||||
panic ("CreateEvent in msleep (%s) failed: %E", wmesg);
|
panic ("CreateEvent in msleep (%s) failed: %E", wmesg);
|
||||||
if (mtx)
|
if (mtx)
|
||||||
@ -199,6 +236,7 @@ _msleep (void *ident, struct mtx *mtx, int priority,
|
|||||||
if ((priority & PCATCH)
|
if ((priority & PCATCH)
|
||||||
&& td->client->signal_arrived () != INVALID_HANDLE_VALUE)
|
&& td->client->signal_arrived () != INVALID_HANDLE_VALUE)
|
||||||
obj_cnt = 4;
|
obj_cnt = 4;
|
||||||
|
|
||||||
switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE))
|
switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE))
|
||||||
{
|
{
|
||||||
case WAIT_OBJECT_0: /* wakeup() has been called. */
|
case WAIT_OBJECT_0: /* wakeup() has been called. */
|
||||||
@ -220,17 +258,23 @@ _msleep (void *ident, struct mtx *mtx, int priority,
|
|||||||
panic ("wait in msleep (%s) failed, %E", wmesg);
|
panic ("wait in msleep (%s) failed, %E", wmesg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
/* Dismiss event before entering mutex. */
|
|
||||||
/* CV 2004-09-06, Don't dismiss for now.
|
|
||||||
TODO: Dismissing was meant to solve a problem with heavy load but
|
|
||||||
there's no proof that it helps. On the contrary, it breaks msgtest
|
|
||||||
in the testsuite. As long as I don't get a testcase to track that
|
|
||||||
down, I'll keep it that way. */
|
|
||||||
ResetEvent (evt);
|
|
||||||
#endif
|
|
||||||
CloseHandle (evt);
|
CloseHandle (evt);
|
||||||
|
/* wakeup has reset the semaphore to 0. Now indicate that this thread
|
||||||
|
has called CloseHandle (evt) and enter the critical section. The
|
||||||
|
critical section is still hold by wakeup, until all formerly sleeping
|
||||||
|
threads have indicated that the event has been dismissed. That's
|
||||||
|
the signal for wakeup that it's the only thread still holding a
|
||||||
|
handle to the event object. wakeup will then close the last handle
|
||||||
|
and leave the critical section. */
|
||||||
|
ReleaseSemaphore (sem, 1, NULL);
|
||||||
|
WaitForSingleObject (mutex, INFINITE);
|
||||||
|
CloseHandle (sem);
|
||||||
|
ReleaseMutex (mutex);
|
||||||
|
CloseHandle (mutex);
|
||||||
|
|
||||||
set_priority (old_priority);
|
set_priority (old_priority);
|
||||||
|
|
||||||
if (mtx && !(priority & PDROP))
|
if (mtx && !(priority & PDROP))
|
||||||
mtx_lock (mtx);
|
mtx_lock (mtx);
|
||||||
return ret;
|
return ret;
|
||||||
@ -243,22 +287,69 @@ int
|
|||||||
wakeup (void *ident)
|
wakeup (void *ident)
|
||||||
{
|
{
|
||||||
char name[64];
|
char name[64];
|
||||||
msleep_event_name (ident, name);
|
LONG threads;
|
||||||
HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, name);
|
|
||||||
if (!evt)
|
HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_EVENT));
|
||||||
|
if (!evt) /* No thread is waiting. */
|
||||||
{
|
{
|
||||||
/* Another round of different error codes returned by 9x and NT
|
/* Another round of different error codes returned by 9x and NT
|
||||||
systems. Oh boy... */
|
systems. Oh boy... */
|
||||||
if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME)
|
if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME)
|
||||||
|| (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND))
|
|| (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND))
|
||||||
panic ("OpenEvent (%s) in wakeup failed: %E", name);
|
panic ("OpenEvent (%s) in wakeup failed: %E", name);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The mutex is used to indicate an ident specific critical section.
|
||||||
|
The critical section is needed to synchronize access to the
|
||||||
|
semaphore and eventually the event object. The whole idea is
|
||||||
|
that a wakeup is *guaranteed* to wakeup *all* threads. If that's
|
||||||
|
not synchronized, sleeping threads could return into the msleep
|
||||||
|
function before all other threads have called CloseHandle(evt).
|
||||||
|
That's bad, since the event still exists and is signalled! */
|
||||||
|
HANDLE mutex = OpenMutex (MUTEX_ALL_ACCESS, FALSE,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_MUTEX));
|
||||||
|
if (!mutex)
|
||||||
|
panic ("OpenMutex (%s) in wakeup failed: %E", name);
|
||||||
|
WaitForSingleObject (mutex, INFINITE);
|
||||||
|
/* Ok, we're in the critical section now. We create an ident specific
|
||||||
|
semaphore, which is used to synchronize the waiting threads. */
|
||||||
|
HANDLE sem = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_SEM));
|
||||||
|
if (!sem)
|
||||||
|
panic ("OpenSemaphore (%s) in wakeup failed: %E", name);
|
||||||
|
ReleaseSemaphore (sem, 1, &threads);
|
||||||
|
/* `threads' is the number of waiting threads. Now reset the semaphore
|
||||||
|
to 0 and wait for this number of threads to indicate that they have
|
||||||
|
called CloseHandle (evt). Then it's save to do the same here in
|
||||||
|
wakeup, which then means that the event object is destroyed and
|
||||||
|
can get safely recycled. */
|
||||||
|
for (int i = threads + 1; i > 0; --i)
|
||||||
|
WaitForSingleObject (sem, INFINITE);
|
||||||
|
|
||||||
|
if (!SetEvent (evt))
|
||||||
|
panic ("SetEvent (%s) in wakeup failed, %E", name);
|
||||||
|
|
||||||
|
/* Now wait for all threads which were waiting for this wakeup. */
|
||||||
|
while (threads-- > 0)
|
||||||
|
WaitForSingleObject (sem, INFINITE);
|
||||||
|
|
||||||
|
/* Now our handle is the last handle to this event object. */
|
||||||
|
CloseHandle (evt);
|
||||||
|
/* But paranoia rulez, so we check here again. */
|
||||||
|
evt = OpenEvent (EVENT_MODIFY_STATE, FALSE,
|
||||||
|
msleep_event_name (ident, name, MSLEEP_EVENT));
|
||||||
if (evt)
|
if (evt)
|
||||||
{
|
panic ("Event %s has not been destroyed. Obviously I can't count :-(",
|
||||||
if (!SetEvent (evt))
|
name);
|
||||||
panic ("SetEvent (%s) in wakeup failed, %E", name);
|
|
||||||
CloseHandle (evt);
|
CloseHandle (sem);
|
||||||
}
|
|
||||||
|
/* Leave critical section (all of wakeup is critical). */
|
||||||
|
ReleaseMutex (mutex);
|
||||||
|
CloseHandle (mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user