From b80b2c011936f7f075b76b6e59f9e8a5ec49caa1 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 24 Mar 2017 16:45:32 +0100 Subject: cygserver: Revamp thread sleep handling The current implementation is a very simple approach to implement a statically sized sleep queue. The problem is that this code requires a certain amount of synchronization because the slots in the queue are used dynamically. To top it off, the Event objects used for sync'ing are created and destroyed on demand. This is complicated, slow, and error prone. There's also a blatant bug here: The number of slots in the queue was wrongly computed in size. It was too small if XSI IPC was used a lot. Make the code more robust. Let the queue have the right size. Every slot is now used for a specific IPC object. All sync objects (switched to Semaphores) are only created when first required, but never destroyed. This reduces the usage of a critical section to the creation of a new sync object. Signed-off-by: Corinna Vinschen --- winsup/cygserver/bsd_mutex.cc | 144 ++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 95 deletions(-) (limited to 'winsup/cygserver/bsd_mutex.cc') diff --git a/winsup/cygserver/bsd_mutex.cc b/winsup/cygserver/bsd_mutex.cc index f21e4d7e9..b95692ba9 100644 --- a/winsup/cygserver/bsd_mutex.cc +++ b/winsup/cygserver/bsd_mutex.cc @@ -13,9 +13,11 @@ details. */ #include #include #include +#include #include #include +#include "bsd_helper.h" #include "process.h" #include "cygserver_ipc.h" @@ -172,141 +174,91 @@ class msleep_sync_array }; CRITICAL_SECTION cs; - long cnt; - long max_cnt; - struct msleep_record { - void *ident; - HANDLE wakeup_evt; - LONG threads; - } *a; - - int find_ident (void *ident, msleep_action action) - { - int i; - for (i = 0; i < cnt; ++i) - if (a[i].ident == ident) - return i; - if (i >= max_cnt) - panic ("ident %x not found and run out of slots.", ident); - if (i >= cnt && action == MSLEEP_LEAVE) - panic ("ident %x not found (%d).", ident, action); - return i; - } - - HANDLE first_entry (int i, void *ident) - { - debug ("New ident %x, index %d", ident, i); - a[i].ident = ident; - a[i].wakeup_evt = CreateEvent (NULL, TRUE, FALSE, NULL); - if (!a[i].wakeup_evt) - panic ("CreateEvent failed: %u", GetLastError ()); - debug ("i = %d, CreateEvent: %x", i, a[i].wakeup_evt); - a[i].threads = 1; - ++cnt; - return a[i].wakeup_evt; - } - - HANDLE next_entry (int i) - { - if (a[i].ident && WaitForSingleObject (a[i].wakeup_evt, 0) != WAIT_OBJECT_0) - { - ++a[i].threads; - return a[i].wakeup_evt; - } - return NULL; - } + PHANDLE wakeup_evt; public: - msleep_sync_array (int count) : cnt (0), max_cnt (count) + msleep_sync_array (int count) { InitializeCriticalSection (&cs); - if (!(a = new msleep_record[count])) + wakeup_evt = (PHANDLE) calloc (count, sizeof (HANDLE)); + if (!wakeup_evt) panic ("Allocating msleep records failed: %d", errno); } - ~msleep_sync_array () { delete a; } + ~msleep_sync_array () { free (wakeup_evt); } - HANDLE enter (void *ident) + HANDLE enter (int idx) { - HANDLE evt = NULL; - while (!evt) + if (!wakeup_evt[idx]) { - EnterCriticalSection (&cs); - int i = find_ident (ident, MSLEEP_ENTER); - if (i >= cnt) - evt = first_entry (i, ident); - else if (!(evt = next_entry (i))) + EnterCriticalSection (&cs); + if (!wakeup_evt[idx]) { - /* wakeup has been called, so sleep to wait until all - formerly waiting threads have left and retry. */ - LeaveCriticalSection (&cs); - Sleep (1L); + wakeup_evt[idx] = CreateSemaphore (NULL, 0, 1024, NULL); + if (!wakeup_evt[idx]) + panic ("CreateSemaphore failed: %u", GetLastError ()); } + LeaveCriticalSection (&cs); } - LeaveCriticalSection (&cs); - return evt; + return wakeup_evt[idx]; } - void leave (void *ident) + void leave (int idx) { - EnterCriticalSection (&cs); - int i = find_ident (ident, MSLEEP_LEAVE); - if (--a[i].threads == 0) - { - debug ("i = %d, CloseEvent: %x", i, a[i].wakeup_evt); - CloseHandle (a[i].wakeup_evt); - a[i].ident = NULL; - --cnt; - if (i < cnt) - a[i] = a[cnt]; - } - LeaveCriticalSection (&cs); + /* Placeholder */ } - void wakeup (void *ident) + void wakeup (int idx) { - EnterCriticalSection (&cs); - int i = find_ident (ident, MSLEEP_WAKEUP); - if (i < cnt && a[i].ident) - SetEvent (a[i].wakeup_evt); - LeaveCriticalSection (&cs); + ReleaseSemaphore (wakeup_evt[idx], 1, NULL); } }; static msleep_sync_array *msleep_sync; +extern struct msginfo msginfo; +extern struct seminfo seminfo; +extern struct shminfo shminfo; +int32_t mni[3]; +int32_t off[3]; + 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: %u", GetLastError ()); - int32_t msgmni = support_msgqueues ? msginfo.msgmni : 0; - int32_t 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_sync = new msleep_sync_array (msgmni + semmni); + mni[SHM] = support_sharedmem ? shminfo.shmmni : 0; + mni[MSQ] = support_msgqueues ? msginfo.msgmni : 0; + mni[SEM] = support_semaphores ? seminfo.semmni : 0; + TUNABLE_INT_FETCH ("kern.ipc.shmmni", &mni[SHM]); + TUNABLE_INT_FETCH ("kern.ipc.msgmni", &mni[MSQ]); + TUNABLE_INT_FETCH ("kern.ipc.semmni", &mni[SEM]); + debug ("Allocating shmmni (%d) + msgmni (%d) + semmni (%d) msleep records", + mni[SHM], mni[MSQ], mni[SEM]); + msleep_sync = new msleep_sync_array (mni[SHM] + mni[MSQ] + mni[SEM]); if (!msleep_sync) panic ("Allocating msleep records in msleep_init failed: %d", errno); + /* Convert mni values to offsets. */ + off[SHM] = 0; + off[MSQ] = mni[SHM]; + off[SEM] = mni[SHM] + mni[MSQ]; } int -_msleep (void *ident, struct mtx *mtx, int priority, +_sleep (ipc_type type, int ident, struct mtx *mtx, int priority, const char *wmesg, int timo, struct thread *td) { int ret = -1; - HANDLE evt = msleep_sync->enter (ident); + HANDLE evt = msleep_sync->enter (off[type] + ident); if (mtx) mtx_unlock (mtx); + int old_priority = set_priority (priority); + HANDLE obj[4] = { evt, @@ -319,6 +271,7 @@ _msleep (void *ident, struct mtx *mtx, int priority, int obj_cnt = 3; if ((priority & PCATCH) && obj[3]) obj_cnt = 4; + switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE)) { case WAIT_OBJECT_0: /* wakeup() has been called. */ @@ -354,12 +307,13 @@ _msleep (void *ident, struct mtx *mtx, int priority, break; } - msleep_sync->leave (ident); - set_priority (old_priority); if (mtx && !(priority & PDROP)) mtx_lock (mtx); + + msleep_sync->leave (off[type] + ident); + return ret; } @@ -367,9 +321,9 @@ _msleep (void *ident, struct mtx *mtx, int priority, * Make all threads sleeping on the specified identifier runnable. */ int -wakeup (void *ident) +_wakeup (ipc_type type, int ident) { - msleep_sync->wakeup (ident); + msleep_sync->wakeup (off[type] + ident); return 0; } -- cgit v1.2.3