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 <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2017-03-24 16:45:32 +01:00
parent 0b73dba4de
commit b80b2c0119
5 changed files with 71 additions and 99 deletions

View File

@ -13,9 +13,11 @@ details. */
#include <sys/smallprint.h> #include <sys/smallprint.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/shm.h>
#include <sys/msg.h> #include <sys/msg.h>
#include <sys/sem.h> #include <sys/sem.h>
#include "bsd_helper.h"
#include "process.h" #include "process.h"
#include "cygserver_ipc.h" #include "cygserver_ipc.h"
@ -172,141 +174,91 @@ class msleep_sync_array
}; };
CRITICAL_SECTION cs; CRITICAL_SECTION cs;
long cnt; PHANDLE wakeup_evt;
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;
}
public: public:
msleep_sync_array (int count) : cnt (0), max_cnt (count) msleep_sync_array (int count)
{ {
InitializeCriticalSection (&cs); 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); 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; if (!wakeup_evt[idx])
while (!evt)
{ {
EnterCriticalSection (&cs); EnterCriticalSection (&cs);
int i = find_ident (ident, MSLEEP_ENTER); if (!wakeup_evt[idx])
if (i >= cnt)
evt = first_entry (i, ident);
else if (!(evt = next_entry (i)))
{ {
/* wakeup has been called, so sleep to wait until all wakeup_evt[idx] = CreateSemaphore (NULL, 0, 1024, NULL);
formerly waiting threads have left and retry. */ if (!wakeup_evt[idx])
LeaveCriticalSection (&cs); panic ("CreateSemaphore failed: %u", GetLastError ());
Sleep (1L);
} }
LeaveCriticalSection (&cs);
} }
LeaveCriticalSection (&cs); return wakeup_evt[idx];
return evt;
} }
void leave (void *ident) void leave (int idx)
{ {
EnterCriticalSection (&cs); /* Placeholder */
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);
} }
void wakeup (void *ident) void wakeup (int idx)
{ {
EnterCriticalSection (&cs); ReleaseSemaphore (wakeup_evt[idx], 1, NULL);
int i = find_ident (ident, MSLEEP_WAKEUP);
if (i < cnt && a[i].ident)
SetEvent (a[i].wakeup_evt);
LeaveCriticalSection (&cs);
} }
}; };
static msleep_sync_array *msleep_sync; 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 void
msleep_init (void) msleep_init (void)
{ {
extern struct msginfo msginfo;
extern struct seminfo seminfo;
msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL);
if (!msleep_glob_evt) if (!msleep_glob_evt)
panic ("CreateEvent in msleep_init failed: %u", GetLastError ()); panic ("CreateEvent in msleep_init failed: %u", GetLastError ());
int32_t msgmni = support_msgqueues ? msginfo.msgmni : 0; mni[SHM] = support_sharedmem ? shminfo.shmmni : 0;
int32_t semmni = support_semaphores ? seminfo.semmni : 0; mni[MSQ] = support_msgqueues ? msginfo.msgmni : 0;
TUNABLE_INT_FETCH ("kern.ipc.msgmni", &msgmni); mni[SEM] = support_semaphores ? seminfo.semmni : 0;
TUNABLE_INT_FETCH ("kern.ipc.semmni", &semmni); TUNABLE_INT_FETCH ("kern.ipc.shmmni", &mni[SHM]);
debug ("Try allocating msgmni (%d) + semmni (%d) msleep records", TUNABLE_INT_FETCH ("kern.ipc.msgmni", &mni[MSQ]);
msgmni, semmni); TUNABLE_INT_FETCH ("kern.ipc.semmni", &mni[SEM]);
msleep_sync = new msleep_sync_array (msgmni + semmni); 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) if (!msleep_sync)
panic ("Allocating msleep records in msleep_init failed: %d", errno); 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 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) const char *wmesg, int timo, struct thread *td)
{ {
int ret = -1; int ret = -1;
HANDLE evt = msleep_sync->enter (ident); HANDLE evt = msleep_sync->enter (off[type] + ident);
if (mtx) if (mtx)
mtx_unlock (mtx); mtx_unlock (mtx);
int old_priority = set_priority (priority); int old_priority = set_priority (priority);
HANDLE obj[4] = HANDLE obj[4] =
{ {
evt, evt,
@ -319,6 +271,7 @@ _msleep (void *ident, struct mtx *mtx, int priority,
int obj_cnt = 3; int obj_cnt = 3;
if ((priority & PCATCH) && obj[3]) if ((priority & PCATCH) && obj[3])
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. */
@ -354,12 +307,13 @@ _msleep (void *ident, struct mtx *mtx, int priority,
break; break;
} }
msleep_sync->leave (ident);
set_priority (old_priority); set_priority (old_priority);
if (mtx && !(priority & PDROP)) if (mtx && !(priority & PDROP))
mtx_lock (mtx); mtx_lock (mtx);
msleep_sync->leave (off[type] + ident);
return ret; return ret;
} }
@ -367,9 +321,9 @@ _msleep (void *ident, struct mtx *mtx, int priority,
* Make all threads sleeping on the specified identifier runnable. * Make all threads sleeping on the specified identifier runnable.
*/ */
int int
wakeup (void *ident) _wakeup (ipc_type type, int ident)
{ {
msleep_sync->wakeup (ident); msleep_sync->wakeup (off[type] + ident);
return 0; return 0;
} }

View File

@ -26,6 +26,12 @@ struct mtx {
unsigned long cnt; unsigned long cnt;
}; };
enum ipc_type {
SHM,
MSQ,
SEM
};
/* Some BSD kernel global mutex. */ /* Some BSD kernel global mutex. */
extern struct mtx Giant; extern struct mtx Giant;
@ -41,10 +47,10 @@ void _mtx_unlock (mtx *, const char *, int);
void mtx_destroy (mtx *); void mtx_destroy (mtx *);
void msleep_init (void); void msleep_init (void);
int _msleep (void *, struct mtx *, int, const char *, int, struct thread *); int _sleep (ipc_type, int, struct mtx *, int, const char *, int, struct thread *);
#define msleep(i,m,p,w,t) _msleep((i),(m),(p),(w),(t),(td)) #define _msleep(T,i,m,p,w,t) _sleep((T),(i),(m),(p),(w),(t),(td))
#define tsleep(i,p,w,t) _msleep((i),NULL,(p),(w),(t),(td)) #define _tsleep(T,i,p,w,t) _sleep((T),(i),NULL,(p),(w),(t),(td))
int wakeup (void *); int _wakeup (ipc_type, int);
void wakeup_all (void); void wakeup_all (void);
#endif /* _BSD_MUTEX_H */ #endif /* _BSD_MUTEX_H */

View File

@ -46,6 +46,9 @@ __FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/kern/sysv_msg.c,v 1.5
#ifdef __CYGWIN__ #ifdef __CYGWIN__
#define MSG_DEBUG #define MSG_DEBUG
#define _mk_msgid(P) ((P) - msqids)
#define msleep(P,m,p,w,t) _msleep(MSQ,_mk_msgid(P),(m),(p),(w),(t))
#define wakeup(P) _wakeup(MSQ,_mk_msgid(P))
#endif /* __CYGWIN__ */ #endif /* __CYGWIN__ */
#ifdef MSG_DEBUG #ifdef MSG_DEBUG

View File

@ -43,6 +43,9 @@ __FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/kern/sysv_sem.c,v 1.70 2004/05/30 20
#define __semctl semctl #define __semctl semctl
#define __semctl_args semctl_args #define __semctl_args semctl_args
#define SEM_DEBUG #define SEM_DEBUG
#define _mk_semid(P) ((P) - sema)
#define msleep(P,m,p,w,t) _msleep(SEM,_mk_semid(P),(m),(p),(w),(t))
#define wakeup(P) _wakeup(SEM,_mk_semid(P))
#endif /* __CYGWIN__ */ #endif /* __CYGWIN__ */
#ifdef SEM_DEBUG #ifdef SEM_DEBUG

View File

@ -59,6 +59,12 @@ __FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/kern/sysv_shm.c,v 1.89 2003/11/07 04
#include "process.h" #include "process.h"
#include "cygserver_ipc.h" #include "cygserver_ipc.h"
#ifdef __CYGWIN__
#define _mk_shmid(P) ((P) - shmsegs)
#define tsleep(P,p,w,t) _tsleep(SHM,_mk_shmid(P),(p),(w),(t))
#define wakeup(P) _wakeup(SHM,_mk_shmid(P))
#endif
#ifdef __CYGWIN__ #ifdef __CYGWIN__
#ifndef PAGE_SIZE #ifndef PAGE_SIZE
#define PAGE_SIZE (getpagesize ()) #define PAGE_SIZE (getpagesize ())