diff options
Diffstat (limited to 'winsup/cygserver/bsd_mutex.cc')
-rw-r--r-- | winsup/cygserver/bsd_mutex.cc | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/winsup/cygserver/bsd_mutex.cc b/winsup/cygserver/bsd_mutex.cc new file mode 100644 index 000000000..9c7485b7f --- /dev/null +++ b/winsup/cygserver/bsd_mutex.cc @@ -0,0 +1,253 @@ +/* bsd_mutex.cc + + Copyright 2003 Red Hat Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" +#include "cygerrno.h" +#define _KERNEL 1 +#define __BSD_VISIBLE 1 +#include <sys/smallprint.h> + +#include "process.h" +#include "cygserver_ipc.h" + +/* A BSD kernel global mutex. */ +struct mtx Giant; + +void +mtx_init (mtx *m, const char *name, const void *, int) +{ + m->name = name; + m->owner = 0; + /* Can't use Windows Mutexes here since Windows Mutexes are only + unlockable by the lock owner. */ + m->h = CreateSemaphore (NULL, 1, 1, NULL); + if (!m->h) + panic ("couldn't allocate %s mutex, %E\n", name); +} + +void +_mtx_lock (mtx *m, DWORD winpid, const char *file, int line) +{ + _log (file, line, LOG_DEBUG, "Try locking mutex %s", m->name); + 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); +} + +int +mtx_owned (mtx *m) +{ + return m->owner > 0; +} + +void +_mtx_assert(mtx *m, int what, const char *file, int line) +{ + switch (what) + { + case MA_OWNED: + if (!mtx_owned (m)) + _panic(file, line, "Mutex %s not owned", m->name); + break; + case MA_NOTOWNED: + if (mtx_owned (m)) + _panic(file, line, "Mutex %s is owned", m->name); + break; + default: + break; + } +} + +void +_mtx_unlock (mtx *m, const char *file, int line) +{ + m->owner = 0; + /* Cautiously check if mtx_destroy has been called (shutdown). + In that case, m->h is NULL. */ + if (m->h && !ReleaseSemaphore (m->h, 1, NULL)) + { + /* Check if the semaphore was already on it's max value. In this case, + ReleaseSemaphore returns FALSE with an error code which *sic* depends + on the OS. */ + if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_PARAMETER) + || (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); +} + +void +mtx_destroy (mtx *m) +{ + HANDLE tmp = m->h; + m->h = NULL; + if (tmp) + CloseHandle (tmp); +} + +/* + * Helper functions for msleep/wakeup. + */ +static char * +msleep_event_name (void *ident, char *name) +{ + if (wincap.has_terminal_services ()) + __small_sprintf (name, "Global\\cygserver.msleep.evt.%08x", ident); + else + __small_sprintf (name, "cygserver.msleep.evt.%08x", ident); + return name; +} + +/* + * Original description from BSD code: + * + * General sleep call. Suspends the current process until a wakeup is + * performed on the specified identifier. The process will then be made + * runnable with the specified priority. Sleeps at most timo/hz seconds + * (0 means no timeout). If pri includes PCATCH flag, signals are checked + * before and after sleeping, else signals are not checked. Returns 0 if + * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a + * signal needs to be delivered, ERESTART is returned if the current system + * call should be restarted if possible, and EINTR is returned if the system + * call should be interrupted by the signal (return EINTR). + * + * The mutex argument is exited before the caller is suspended, and + * entered before msleep returns. If priority includes the PDROP + * flag the mutex is not entered before returning. + */ +static HANDLE msleep_glob_evt; + +void +msleep_init (void) +{ + msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); + if (!msleep_glob_evt) + panic ("CreateEvent in msleep_init failed: %E"); +} + +static int +win_priority (int priority) +{ + int p = (int)((p) & PRIO_MASK) - PZERO; + /* Generating a valid priority value is a bit tricky. The only valid + values on 9x and NT4 are -15, -2, -1, 0, 1, 2, 15. */ + switch (p) + { + case -15: case -14: case -13: case -12: case -11: + return THREAD_PRIORITY_IDLE; + case -10: case -9: case -8: case -7: case -6: + return THREAD_PRIORITY_LOWEST; + case -5: case -4: case -3: case -2: case -1: + return THREAD_PRIORITY_BELOW_NORMAL; + case 0: + return THREAD_PRIORITY_NORMAL; + case 1: case 2: case 3: case 4: case 5: + return THREAD_PRIORITY_ABOVE_NORMAL; + case 6: case 7: case 8: case 9: case 10: + return THREAD_PRIORITY_HIGHEST; + case 11: case 12: case 13: case 14: case 15: + return THREAD_PRIORITY_TIME_CRITICAL; + } + return THREAD_PRIORITY_NORMAL; +} + +/* + * Sets the thread priority, returns the old priority. + */ +static int +set_priority (int priority) +{ + int old_prio = GetThreadPriority (GetCurrentThread ()); + if (!SetThreadPriority (GetCurrentThread (), win_priority(priority))) + log (LOG_WARNING, + "Warning: Setting thread priority to %d failed with error %lu\n", + win_priority(priority), GetLastError ()); + return old_prio; +} + +int +_msleep (void *ident, struct mtx *mtx, int priority, + const char *wmesg, int timo, struct thread *td) +{ + int ret = -1; + char name[64]; + msleep_event_name (ident, name); + HANDLE evt = OpenEvent (EVENT_ALL_ACCESS, FALSE, name); + if (!evt) + evt = CreateEvent (NULL, TRUE, FALSE, name); + if (!evt) + panic ("CreateEvent in msleep (%s) failed: %E", wmesg); + if (mtx) + mtx_unlock (mtx); + int old_priority = set_priority (priority); + /* PCATCH can't be handled here. */ + HANDLE obj[3] = { evt, td->client->handle (), msleep_glob_evt }; + switch (WaitForMultipleObjects (3, obj, FALSE, timo ?: INFINITE)) + { + case WAIT_OBJECT_0: /* wakeup() has been called. */ + ret = 0; + break; + case WAIT_OBJECT_0 + 2: /* Shutdown event (triggered by wakeup_all). */ + priority |= PDROP; + /*FALLTHRU*/ + case WAIT_OBJECT_0 + 1: /* The dependent process has exited. */ + ret = EIDRM; + break; + case WAIT_TIMEOUT: + ret = EWOULDBLOCK; + break; + default: + panic ("wait in msleep (%s) failed, %E", wmesg); + break; + } + set_priority (old_priority); + if (!(priority & PDROP) && mtx) + mtx_lock (mtx); + CloseHandle (evt); + return ret; +} + +/* + * Make all threads sleeping on the specified identifier runnable. + */ +int +wakeup (void *ident) +{ + char name[64]; + msleep_event_name (ident, name); + HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, name); + if (!evt) + { + /* 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); + } + if (evt) + { + if (!SetEvent (evt)) + panic ("SetEvent (%s) in wakeup failed, %E", name); + CloseHandle (evt); + } + return 0; +} + +/* + * Wakeup all sleeping threads. Only called in the context of cygserver + * shutdown. + */ +void +wakeup_all (void) +{ + SetEvent (msleep_glob_evt); +} +#endif /* __OUTSIDE_CYGWIN__ */ |