summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Li <Frank.Li@nxp.com>2019-08-15 10:21:17 -0500
committerNathan Hjelm <hjelmn@google.com>2019-10-30 19:05:42 -0700
commitc730a8410c8fc683e845fefcc06d6ced349b583b (patch)
treeaa9c527644fb07910a4ae847e5d149d153019a37
parent211d79185120519ca9c436855d3b7406359e793b (diff)
downloadlibusb-c730a8410c8fc683e845fefcc06d6ced349b583b.tar.gz
windows: workaround WaitForMultipleObjects max 64 events limitation.
WaitForMultiObjects have limiation. Only can wait max 64 events. but usbi_poll may pass more than 64 fds. In previous implement, only wait for first 64 events. if previous 64 events were not trigger usbi_poll will wait for about 10s timemout eventhough other event triggered. This patch workaround this limitation. If max events less than 64, call WaitforMultiObjects directly. If max events more than 64, group every 63 events into one work thread. This thread call waitformulitobjects wait for this groug events and one addtional thread exit events. If any events trigger, this thread will trigger main notify events. The main usbi_poll thread call waitforsingleobject wait for notify events. If this events trigger, that means any of work threads get events. Then call exit notify events let all working thread exit safely. Return value changed, 0 means timeout. 1 - N means which event triggered. Closes #612 Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Nathan Hjelm <hjelmn@google.com>
-rw-r--r--libusb/os/poll_windows.c122
-rw-r--r--libusb/version_nano.h2
2 files changed, 115 insertions, 9 deletions
diff --git a/libusb/os/poll_windows.c b/libusb/os/poll_windows.c
index 243495b..2a3734a 100644
--- a/libusb/os/poll_windows.c
+++ b/libusb/os/poll_windows.c
@@ -225,10 +225,6 @@ static int check_pollfds(struct pollfd *fds, unsigned int nfds,
fds[n].revents = fds[n].events;
nready++;
} else if (wait_handles != NULL) {
- if (*nb_wait_handles == MAXIMUM_WAIT_OBJECTS) {
- usbi_warn(NULL, "too many HANDLEs to wait on");
- continue;
- }
wait_handles[*nb_wait_handles] = fd->overlapped.hEvent;
(*nb_wait_handles)++;
}
@@ -238,6 +234,108 @@ static int check_pollfds(struct pollfd *fds, unsigned int nfds,
return nready;
}
+
+#define EXT_TIMEOUT WAIT_OBJECT_0
+
+struct ExThreadData
+{
+ HANDLE notifyevents;
+
+ HANDLE thread;
+ HANDLE wait_event[MAXIMUM_WAIT_OBJECTS];
+ int nEvents;
+ DWORD ret_wait;
+ volatile int bexit;
+};
+
+static DWORD __stdcall WaitThread(LPVOID lpThreadParameter)
+{
+ struct ExThreadData *p = (struct ExThreadData *)lpThreadParameter;
+ int ret = WaitForMultipleObjects(p->nEvents, p->wait_event, FALSE, INFINITE);
+ p->ret_wait = ret;
+ p->bexit = true;
+ SetEvent(p->notifyevents);
+ return 0;
+}
+
+static DWORD ExtendWaitForMultipleObjects(
+ DWORD nCount,
+ const HANDLE *lpHandles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds
+)
+{
+ DWORD ret;
+ int i = 0;
+ int nThreads = 0;
+ struct ExThreadData *pThread;
+ int size;
+ HANDLE notify_event;
+
+ if (nCount <= MAXIMUM_WAIT_OBJECTS) {
+ ret = WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds);
+ if (ret == WAIT_TIMEOUT)
+ return EXT_TIMEOUT;
+
+ if (ret < WAIT_OBJECT_0 + nCount)
+ return ret + 1;
+
+ return ret;
+ }
+
+ nThreads = (nCount + MAXIMUM_WAIT_OBJECTS - 2) / (MAXIMUM_WAIT_OBJECTS - 1);
+
+ notify_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (notify_event == NULL) {
+ usbi_err(NULL, "Create Event failure");
+ return WAIT_FAILED;
+ }
+
+ pThread = malloc(sizeof(struct ExThreadData) * nThreads);
+
+ if (pThread == NULL) {
+ usbi_err(NULL, "Out of memory");
+ CloseHandle(notify_event);
+ return WAIT_FAILED;
+ }
+
+ for (i = 0; i < nThreads; i++)
+ {
+ pThread[i].wait_event[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ pThread[i].notifyevents = notify_event;
+
+ size = nCount - i * (MAXIMUM_WAIT_OBJECTS - 1);
+ if (size >= (MAXIMUM_WAIT_OBJECTS - 1))
+ size = (MAXIMUM_WAIT_OBJECTS - 1);
+
+ memcpy(pThread[i].wait_event + 1, lpHandles + i * (MAXIMUM_WAIT_OBJECTS - 1), size * sizeof(HANDLE));
+
+ pThread[i].nEvents = size + 1;
+
+ pThread[i].bexit = 0;
+
+ pThread[i].thread = CreateThread(NULL, 0, WaitThread, pThread+i, 0, NULL);
+ }
+
+ ret = WaitForSingleObject(notify_event, INFINITE);
+
+ for (i = 0; i < nThreads; i++)
+ {
+ SetEvent(pThread[i].wait_event[0]);
+ while (pThread[i].bexit == 0); //wait for thread exist;
+
+ if (pThread[i].ret_wait != WAIT_OBJECT_0)
+ ret = pThread[i].ret_wait + i * (MAXIMUM_WAIT_OBJECTS - 1);
+
+ CloseHandle(pThread[i].wait_event[0]);
+ }
+
+ CloseHandle(notify_event);
+ free(pThread);
+
+ return ret ;
+}
+
/*
* POSIX poll equivalent, using Windows OVERLAPPED
* Currently, this function only accepts one of POLLIN or POLLOUT per fd
@@ -245,26 +343,34 @@ static int check_pollfds(struct pollfd *fds, unsigned int nfds,
*/
int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout)
{
- HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
+ HANDLE *wait_handles;
DWORD nb_wait_handles = 0;
DWORD ret;
int nready;
+ wait_handles = malloc(nfds * sizeof(HANDLE));
+ if (!wait_handles)
+ {
+ usbi_err(NULL, "Out of memory");
+ return -1;
+ }
+
nready = check_pollfds(fds, nfds, wait_handles, &nb_wait_handles);
// If nothing was triggered, wait on all fds that require it
if ((nready == 0) && (nb_wait_handles != 0) && (timeout != 0)) {
- ret = WaitForMultipleObjects(nb_wait_handles, wait_handles,
+ ret = ExtendWaitForMultipleObjects(nb_wait_handles, wait_handles,
FALSE, (timeout < 0) ? INFINITE : (DWORD)timeout);
- if (ret < (WAIT_OBJECT_0 + nb_wait_handles)) {
+ if (ret != EXT_TIMEOUT && ret <= (WAIT_OBJECT_0 + nb_wait_handles)) {
nready = check_pollfds(fds, nfds, NULL, NULL);
- } else if (ret != WAIT_TIMEOUT) {
+ } else if (ret != EXT_TIMEOUT) {
if (ret == WAIT_FAILED)
usbi_err(NULL, "WaitForMultipleObjects failed: %u", (unsigned int)GetLastError());
nready = -1;
}
}
+ free(wait_handles);
return nready;
}
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index dcba758..dcea538 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11407
+#define LIBUSB_NANO 11408