diff --git a/src/w32.h b/src/w32.h index 84059278a2a..73429cf6731 100644 --- a/src/w32.h +++ b/src/w32.h @@ -29,7 +29,7 @@ #define EMACS_W32_H /* File descriptor set emulation. */ /* MSVC runtime library has limit of 64 descriptors by default */ -#define FD_SETSIZE 64 +#define FD_SETSIZE (MAXIMUM_WAIT_OBJECTS * MAXIMUM_WAIT_OBJECTS / 2) typedef struct { unsigned int bits[FD_SETSIZE / 32]; } fd_set; diff --git a/src/w32proc.c b/src/w32proc.c index 000eb9bee3f..29c523c2249 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -1012,6 +1012,189 @@ alarm (int seconds) shell might not be killed, because sys_kill will only terminate the shell. (In practice, however, such problems are very rare.) */ +#define WAIT_TIMEOUT_2 (WAIT_TIMEOUT | 0x4000) +#define WAIT_ABANDONED_0_2 (WAIT_ABANDONED_0 | 0x4000) +#define WAIT_GROUP_SIZE (MAXIMUM_WAIT_OBJECTS - 1) +#define MAXIMUM_WAIT_OBJECTS_2 FD_SETSIZE + +typedef struct { + HANDLE* lpHandles; + int nCount; + BOOL bWaitAll; + DWORD dwMilliseconds; + int nIndex; +} WaitForMultipleObjectsParams; + +static DWORD WINAPI +WaitForMultipleObjectsWrapped (LPVOID lpParam) +{ + WaitForMultipleObjectsParams *p = lpParam; + DWORD result = WaitForMultipleObjects (p->nCount, p->lpHandles, + p->bWaitAll, p->dwMilliseconds); + if (result == WAIT_TIMEOUT) + result = WAIT_TIMEOUT_2; + else if (result >= WAIT_ABANDONED_0 + && result < WAIT_ABANDONED_0 + p->nCount) + result += (WAIT_ABANDONED_0_2 - WAIT_ABANDONED_0) + + WAIT_GROUP_SIZE * p->nIndex; + else if (result >= WAIT_OBJECT_0 + && result < WAIT_OBJECT_0 + p->nCount) + result += WAIT_GROUP_SIZE * p->nIndex; + return result; +} + +typedef struct +{ + HANDLE *hObjects; + WaitForMultipleObjectsParams *pParams; + HANDLE *pHandles; + HANDLE hEndEvent; + int nObject; + int nThread; + int nHandle; +} WaitForMultipleObjectsInfo; + +static void +InitializeWaitForMultipleObjectsInfo (WaitForMultipleObjectsInfo *p, + DWORD nCount, + CONST HANDLE *lpHandles, + BOOL bWaitAll, + DWORD dwMilliseconds) +{ + p->nThread = 1 + (nCount - 1 - MAXIMUM_WAIT_OBJECTS / 2) + / WAIT_GROUP_SIZE; + p->nObject = (p->nThread * WAIT_GROUP_SIZE >= nCount) + ? p->nThread + : p->nThread + (nCount - p->nThread * WAIT_GROUP_SIZE); + p->hObjects = xmalloc (sizeof (HANDLE) * p->nObject); + if (p->nObject != p->nThread) + memcpy (p->hObjects + p->nThread, + lpHandles + p->nThread * WAIT_GROUP_SIZE, + sizeof (HANDLE) * (nCount - p->nThread * WAIT_GROUP_SIZE)); + p->pParams = xmalloc (sizeof (WaitForMultipleObjectsParams) * p->nThread); + p->pHandles = xmalloc (sizeof (HANDLE) * p->nThread * MAXIMUM_WAIT_OBJECTS); + p->hEndEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + for (int i = 0, offset = 0; i < p->nThread; i++) + { + int count = (i == p->nThread - 1) + ? (p->nThread == p->nObject) + ? (nCount - i * WAIT_GROUP_SIZE) + : WAIT_GROUP_SIZE + : WAIT_GROUP_SIZE; + p->pParams[i].nCount = count + 1; + p->pParams[i].lpHandles = p->pHandles + i * MAXIMUM_WAIT_OBJECTS; + p->pParams[i].dwMilliseconds = dwMilliseconds; + p->pParams[i].bWaitAll = bWaitAll; + p->pParams[i].nIndex = i; + memcpy (p->pParams[i].lpHandles, lpHandles + offset, + sizeof (HANDLE) * count); + p->pParams[i].lpHandles[count] = p->hEndEvent; + offset += count; + p->hObjects[i] = CreateThread (NULL, 0, WaitForMultipleObjectsWrapped, + p->pParams + i, 0, NULL); + } + return; +} + +static void +CleanupWaitForMultipleObjectsInfo (WaitForMultipleObjectsInfo *p) +{ + SetEvent (p->hEndEvent); + WaitForMultipleObjects(p->nThread, p->hObjects, TRUE, INFINITE); + + xfree (p->hObjects); + xfree (p->pHandles); + xfree (p->pParams); + CloseHandle (p->hEndEvent); + for (int i = 0; i < p->nThread; i++) + CloseHandle (p->hObjects[i]); + return; +} + +static DWORD +ExtractWaitResult (WaitForMultipleObjectsInfo *pinfo, DWORD result) +{ + if (result == WAIT_TIMEOUT) + return WAIT_TIMEOUT_2; + else if (result == WAIT_FAILED) + return WAIT_FAILED; + else if (result >= WAIT_OBJECT_0 + && result < WAIT_OBJECT_0 + pinfo->nObject) + { + if (result >= WAIT_OBJECT_0 + pinfo->nThread) + return result - pinfo->nThread + + pinfo->nThread * WAIT_GROUP_SIZE; + DWORD code; + GetExitCodeThread(pinfo->hObjects[result], &code); + return code; + } + else if (result >= WAIT_ABANDONED_0 + && result < WAIT_ABANDONED_0 + pinfo->nObject) + { + if (result >= WAIT_ABANDONED_0 + pinfo->nThread) + return result + WAIT_ABANDONED_0_2 - WAIT_ABANDONED_0 + - pinfo->nThread + pinfo->nThread * WAIT_GROUP_SIZE; + } + /* Should never happen */ + return result; +} + +static DWORD +WaitForMultipleObjectsThreaded (DWORD nCount, + HANDLE *lpHandles, + BOOL bWaitAll, + DWORD dwMilliseconds) +{ + if (nCount <= MAXIMUM_WAIT_OBJECTS) + { + DWORD result = WaitForMultipleObjects (nCount, lpHandles, bWaitAll, + dwMilliseconds); + if (result == WAIT_TIMEOUT) + result = WAIT_TIMEOUT_2; + else if (result >= WAIT_ABANDONED_0 + && result < WAIT_ABANDONED_0 + nCount) + result += WAIT_ABANDONED_0_2 - WAIT_ABANDONED_0; + return result; + } + WaitForMultipleObjectsInfo info; + InitializeWaitForMultipleObjectsInfo (&info, nCount, lpHandles, + bWaitAll, dwMilliseconds); + DWORD result = WaitForMultipleObjects (info.nObject, info.hObjects, + bWaitAll, dwMilliseconds); + result = ExtractWaitResult (&info, result); + CleanupWaitForMultipleObjectsInfo (&info); + return result; +} + +static DWORD +MsgWaitForMultipleObjectsThreaded (DWORD nCount, + HANDLE *lpHandles, + BOOL bWaitAll, + DWORD dwMilliseconds, + DWORD dwWakeMask) +{ + if (nCount <= MAXIMUM_WAIT_OBJECTS - 1) + { + DWORD result = MsgWaitForMultipleObjects (nCount, lpHandles, bWaitAll, + dwMilliseconds, dwWakeMask); + if (result == WAIT_TIMEOUT) + result = WAIT_TIMEOUT_2; + else if (result >= WAIT_ABANDONED_0 + && result < WAIT_ABANDONED_0 + nCount) + result += WAIT_ABANDONED_0_2 - WAIT_ABANDONED_0; + return result; + } + WaitForMultipleObjectsInfo info; + InitializeWaitForMultipleObjectsInfo (&info, nCount, + lpHandles, bWaitAll, dwMilliseconds); + DWORD result = MsgWaitForMultipleObjects (info.nObject, info.hObjects, + bWaitAll, dwMilliseconds, + dwWakeMask); + result = ExtractWaitResult (&info, result); + CleanupWaitForMultipleObjectsInfo (&info); + return result; +} + /* Defined in which conflicts with the local copy */ #define _P_NOWAIT 1 @@ -1566,15 +1749,15 @@ waitpid (pid_t pid, int *status, int options) quitting in that case. */ if (!dont_wait) maybe_quit (); - active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms); - } while (active == WAIT_TIMEOUT && !dont_wait); + active = WaitForMultipleObjectsThreaded (nh, wait_hnd, FALSE, timeout_ms); + } while (active == WAIT_TIMEOUT_2 && !dont_wait); if (active == WAIT_FAILED) { errno = EBADF; return -1; } - else if (active == WAIT_TIMEOUT && dont_wait) + else if (active == WAIT_TIMEOUT_2 && dont_wait) { /* PID specifies our subprocess, but it didn't exit yet, so its status is not yet available. */ @@ -1584,14 +1767,14 @@ waitpid (pid_t pid, int *status, int options) return 0; } else if (active >= WAIT_OBJECT_0 - && active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS) + && active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS_2) { active -= WAIT_OBJECT_0; } - else if (active >= WAIT_ABANDONED_0 - && active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS) + else if (active >= WAIT_ABANDONED_0_2 + && active < WAIT_ABANDONED_0_2+MAXIMUM_WAIT_OBJECTS_2) { - active -= WAIT_ABANDONED_0; + active -= WAIT_ABANDONED_0_2; } else emacs_abort (); @@ -2501,10 +2684,10 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, /* Wait for input or child death to be signaled. If user input is allowed, then also accept window messages. */ if (FD_ISSET (0, &orfds)) - active = MsgWaitForMultipleObjects (nh + nc, wait_hnd, FALSE, timeout_ms, + active = MsgWaitForMultipleObjectsThreaded (nh + nc, wait_hnd, FALSE, timeout_ms, QS_ALLINPUT); else - active = WaitForMultipleObjects (nh + nc, wait_hnd, FALSE, timeout_ms); + active = WaitForMultipleObjectsThreaded (nh + nc, wait_hnd, FALSE, timeout_ms); if (active == WAIT_FAILED) { @@ -2517,7 +2700,7 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, errno = EINTR; return -1; } - else if (active == WAIT_TIMEOUT) + else if (active == WAIT_TIMEOUT_2) { if (noninteractive) { @@ -2527,14 +2710,14 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, return 0; } else if (active >= WAIT_OBJECT_0 - && active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS) + && active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS_2) { active -= WAIT_OBJECT_0; } - else if (active >= WAIT_ABANDONED_0 - && active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS) + else if (active >= WAIT_ABANDONED_0_2 + && active < WAIT_ABANDONED_0_2+MAXIMUM_WAIT_OBJECTS_2) { - active -= WAIT_ABANDONED_0; + active -= WAIT_ABANDONED_0_2; } else emacs_abort ();