diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2005-04-06 11:11:07 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2005-04-06 11:11:07 +0000 |
commit | dafef5e249a5ddd5e9a935b802e5e61e4cc8aaa9 (patch) | |
tree | fc4915e538b760d069291c553ab4504ccc833869 /winsup/cygserver/bsd_mutex.cc | |
parent | f0fdfd34547a1adefc71237efa9baf4efb8a7e43 (diff) | |
download | cygnal-dafef5e249a5ddd5e9a935b802e5e61e4cc8aaa9.tar.gz cygnal-dafef5e249a5ddd5e9a935b802e5e61e4cc8aaa9.tar.bz2 cygnal-dafef5e249a5ddd5e9a935b802e5e61e4cc8aaa9.zip |
* bsd_helper.cc (ipcexit_hookthread): Fix whitespace and handle leak.
* bsd_mutex.cc: Include stdlib.h, sys/msg.h and sys/sem.h.
(mtx_init): Initialize lock counter to 0.
(_mtx_lock): Increment and log mutex lock counter.
(mtx_owned): Add winpid argument. Return true only if mutex is
actually owned by process winpid.
(_mtx_assert): Add winpid argument accordingly.
(_mtx_unlock): Log owner and lock count.
(MSLEEP_MUTEX): Remove.
(MSLEEP_SEM): Ditto.
(MSLEEP_EVENT): Ditto.
(msleep_event_name): Ditto.
(msleep_cs): New global critical section.
(msleep_cnt): New global variable indicating msleep record usage.
(msleep_max_cnt): New global variable indicating msleep record size.
(msleep_arr): New global pointer to msleep records.
(msleep_init): Initialize msleep_cs. Allocate msleep_arr array.
(_msleep): Rewrite using new msleep_cs/msleep_arr based thread
synchronization. Don't be shy with debug output.
(wakeup): Rewrite using new msleep_cs/msleep_arr based thread
synchronization.
* bsd_mutex.h (struct mtx): Add lock counter for better debugging.
(mtx_owned): Declare with winpid argument.
(_mtx_assert): Ditto.
(mtx_assert): Define with winpid argument.
* cygserver.cc (version): Remove.
(SERVER_VERSION): New define, decoupling server version information
from source code control system.
(print_version): Simplify printing server version.
* process.cc (process::process): Fix wrong bracketing (and handle leak).
(process::~process): Only try to close _signal_arrived if valid.
* sysv_sem.cc: Include sys/smallprint.h.
(semundo_clear): Define with additional struct thread pointer argument.
Accomodate throughout.
(SEMUNDO_LOCKASSERT): Define with winpid argument. Accomodate
throughout.
(struct sem_undo): Define un_proc as pid_t on Cygwin. Accomodate
throughout.
(seminit): Improve debugging by adding the semid to the mutex name.
(semget): Correctly print key value as 64 bit hex value in debug
output.
(semexit_myhook): Remove Cygwin specific unlocking of mutexes owned
by exiting process. Keep semaphore global lock throughout whole
function to avoid races.
* sysv_shm.cc (GIANT_REQUIRED): Define empty on Cygwin. We know that
Giant is locked.
Diffstat (limited to 'winsup/cygserver/bsd_mutex.cc')
-rw-r--r-- | winsup/cygserver/bsd_mutex.cc | 240 |
1 files changed, 105 insertions, 135 deletions
diff --git a/winsup/cygserver/bsd_mutex.cc b/winsup/cygserver/bsd_mutex.cc index 82ce4bf27..a470b26d5 100644 --- a/winsup/cygserver/bsd_mutex.cc +++ b/winsup/cygserver/bsd_mutex.cc @@ -14,6 +14,9 @@ details. */ #define __BSD_VISIBLE 1 #include <sys/smallprint.h> #include <limits.h> +#include <stdlib.h> +#include <sys/msg.h> +#include <sys/sem.h> #include "process.h" #include "cygserver_ipc.h" @@ -26,6 +29,7 @@ mtx_init (mtx *m, const char *name, const void *, int) { m->name = name; m->owner = 0; + m->cnt = 0; /* Can't use Windows Mutexes here since Windows Mutexes are only unlockable by the lock owner. */ m->h = CreateSemaphore (NULL, 1, 1, NULL); @@ -36,30 +40,32 @@ mtx_init (mtx *m, const char *name, const void *, int) void _mtx_lock (mtx *m, DWORD winpid, const char *file, int line) { - _log (file, line, LOG_DEBUG, "Try locking mutex %s", m->name); + _log (file, line, LOG_DEBUG, "Try locking mutex %s (%u) (hold: %u)", + m->name, winpid, m->owner); if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0) _panic (file, line, "wait for %s in %d failed, %E", m->name, winpid); m->owner = winpid; - _log (file, line, LOG_DEBUG, "Locked mutex %s", m->name); + _log (file, line, LOG_DEBUG, "Locked mutex %s/%u (%u)", + m->name, ++m->cnt, winpid); } int -mtx_owned (mtx *m) +mtx_owned (mtx *m, DWORD winpid) { - return m->owner > 0; + return m->owner == winpid; } void -_mtx_assert (mtx *m, int what, const char *file, int line) +_mtx_assert (mtx *m, int what, DWORD winpid, const char *file, int line) { switch (what) { case MA_OWNED: - if (!mtx_owned (m)) + if (!mtx_owned (m, winpid)) _panic (file, line, "Mutex %s not owned", m->name); break; case MA_NOTOWNED: - if (mtx_owned (m)) + if (mtx_owned (m, winpid)) _panic (file, line, "Mutex %s is owned", m->name); break; default: @@ -70,6 +76,8 @@ _mtx_assert (mtx *m, int what, const char *file, int line) void _mtx_unlock (mtx *m, const char *file, int line) { + DWORD owner = m->owner; + unsigned long cnt = m->cnt; m->owner = 0; /* Cautiously check if mtx_destroy has been called (shutdown). In that case, m->h is NULL. */ @@ -82,7 +90,8 @@ _mtx_unlock (mtx *m, const char *file, int line) || (wincap.is_winnt () && GetLastError () != ERROR_TOO_MANY_POSTS)) _panic (file, line, "release of mutex %s failed, %E", m->name); } - _log (file, line, LOG_DEBUG, "Unlocked mutex %s", m->name); + _log (file, line, LOG_DEBUG, "Unlocked mutex %s/%u (owner: %u)", + m->name, cnt, owner); } void @@ -98,22 +107,6 @@ mtx_destroy (mtx *m) * Helper functions for msleep/wakeup. */ -/* Values for which */ -#define MSLEEP_MUTEX 0 -#define MSLEEP_SEM 1 -#define MSLEEP_EVENT 2 - -static char * -msleep_event_name (void *ident, char *name, int which) -{ - if (wincap.has_terminal_services ()) - __small_sprintf (name, "Global\\cygserver.msleep.evt.%1d.%08x", - which, ident); - else - __small_sprintf (name, "cygserver.msleep.evt.%1d.%08x", which, ident); - return name; -} - static int win_priority (int priority) { @@ -172,13 +165,36 @@ set_priority (int priority) * flag the mutex is not entered before returning. */ static HANDLE msleep_glob_evt; +CRITICAL_SECTION msleep_cs; +static long msleep_cnt; +static long msleep_max_cnt; +static struct msleep_record { + void *ident; + HANDLE wakeup_evt; + LONG threads; +} *msleep_arr; void msleep_init (void) { + extern struct msginfo msginfo; + extern struct seminfo seminfo; + msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); if (!msleep_glob_evt) panic ("CreateEvent in msleep_init failed: %E"); + InitializeCriticalSection (&msleep_cs); + long msgmni = support_msgqueues ? msginfo.msgmni : 0; + long semmni = support_semaphores ? seminfo.semmni : 0; + TUNABLE_INT_FETCH ("kern.ipc.msgmni", &msgmni); + TUNABLE_INT_FETCH ("kern.ipc.semmni", &semmni); + debug ("Try allocating msgmni (%d) + semmni (%d) msleep records", + msgmni, semmni); + msleep_max_cnt = msgmni + semmni; + msleep_arr = (struct msleep_record *) calloc (msleep_max_cnt, + sizeof (struct msleep_record)); + if (!msleep_arr) + panic ("Allocating msleep records in msleep_init failed: %d", errno); } int @@ -186,46 +202,47 @@ _msleep (void *ident, struct mtx *mtx, int priority, const char *wmesg, int timo, struct thread *td) { int ret = -1; - char name[64]; - - /* 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); + int i; - /* 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); + while (1) + { + EnterCriticalSection (&msleep_cs); + for (i = 0; i < msleep_cnt; ++i) + if (msleep_arr[i].ident == ident) + break; + if (!msleep_arr[i].ident) + { + debug ("New ident %x, index %d", ident, i); + if (i >= msleep_max_cnt) + panic ("Too many idents to wait for.\n"); + msleep_arr[i].ident = ident; + msleep_arr[i].wakeup_evt = CreateEvent (NULL, TRUE, FALSE, NULL); + if (!msleep_arr[i].wakeup_evt) + panic ("CreateEvent in msleep (%s) failed: %E", wmesg); + msleep_arr[i].threads = 1; + ++msleep_cnt; + LeaveCriticalSection (&msleep_cs); + break; + } + else if (WaitForSingleObject (msleep_arr[i].wakeup_evt, 0) + != WAIT_OBJECT_0) + { + ++msleep_arr[i].threads; + LeaveCriticalSection (&msleep_cs); + break; + } + /* Otherwise wakeup has been called, so sleep to wait until all + formerly waiting threads have left and retry. */ + LeaveCriticalSection (&msleep_cs); + Sleep (1L); + } - HANDLE evt = CreateEvent (NULL, TRUE, FALSE, - msleep_event_name (ident, name, MSLEEP_EVENT)); - if (!evt) - panic ("CreateEvent in msleep (%s) failed: %E", wmesg); if (mtx) mtx_unlock (mtx); int old_priority = set_priority (priority); HANDLE obj[4] = { - evt, + msleep_arr[i].wakeup_evt, msleep_glob_evt, td->client->handle (), td->client->signal_arrived () @@ -241,37 +258,45 @@ _msleep (void *ident, struct mtx *mtx, int priority, { case WAIT_OBJECT_0: /* wakeup() has been called. */ ret = 0; + debug ("msleep wakeup called"); break; case WAIT_OBJECT_0 + 1: /* Shutdown event (triggered by wakeup_all). */ priority |= PDROP; /*FALLTHRU*/ case WAIT_OBJECT_0 + 2: /* The dependent process has exited. */ + debug ("msleep process exit or shutdown"); ret = EIDRM; break; case WAIT_OBJECT_0 + 3: /* Signal for calling process arrived. */ + debug ("msleep process got signal"); ret = EINTR; break; case WAIT_TIMEOUT: ret = EWOULDBLOCK; break; default: - panic ("wait in msleep (%s) failed, %E", wmesg); + /* There's a chance that a process has been terminated before + WaitForMultipleObjects has been called. In this case the handles + might be invalid. The error code returned is ERROR_INVALID_HANDLE. + Since we can trust the values of these handles otherwise, we + treat an ERROR_INVALID_HANDLE as a normal process termination and + hope for the best. */ + if (GetLastError () != ERROR_INVALID_HANDLE) + panic ("wait in msleep (%s) failed, %E", wmesg); + ret = EIDRM; break; } - 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); + EnterCriticalSection (&msleep_cs); + if (--msleep_arr[i].threads == 0) + { + CloseHandle (msleep_arr[i].wakeup_evt); + msleep_arr[i].ident = NULL; + --msleep_cnt; + if (i < msleep_cnt) + msleep_arr[i] = msleep_arr[msleep_cnt]; + } + LeaveCriticalSection (&msleep_cs); set_priority (old_priority); @@ -286,70 +311,15 @@ _msleep (void *ident, struct mtx *mtx, int priority, int wakeup (void *ident) { - char name[64]; - LONG threads; - - 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 - systems. Oh boy... */ - if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME) - || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND)) - 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) - panic ("Event %s has not been destroyed. Obviously I can't count :-(", - name); - - CloseHandle (sem); - - /* Leave critical section (all of wakeup is critical). */ - ReleaseMutex (mutex); - CloseHandle (mutex); - + int i; + + EnterCriticalSection (&msleep_cs); + for (i = 0; i < msleep_cnt; ++i) + if (msleep_arr[i].ident == ident) + break; + if (msleep_arr[i].ident) + SetEvent (msleep_arr[i].wakeup_evt); + LeaveCriticalSection (&msleep_cs); return 0; } |