From df61c0c3a3de070c7f7a714ef8758a248bf97263 Mon Sep 17 00:00:00 2001 From: Chris Dickens Date: Wed, 22 Jan 2020 11:06:29 -0800 Subject: [PATCH] Windows: Improve poll abstraction Commit 395e5a8a6f ("windows: remove total fds (256) limitations") and commit c730a8410c ("windows: workaround WaitForMultipleObjects max 64 events limitation.") lifted some hard-coded limits in the number of HANDLEs that can be used within the library. This change improves on these changes to make them more efficient. A bitmap has been added to provide an efficient lookup mechanism for located unused file descriptor indices. This avoids the O(n) lookup time for traversing the entire fd_table. This bitmap is dynamically resized along with the fd_table. The incremental size of the fd_table has been reduced from 256 to 64. The vast majority of applications won't need to use 256 HANDLEs, so we can optimize memory usage a bit. Commit fb864b7cde ("fix windows crash when multi-thread do sync transfer") added usbi_inc_fds_ref() and usbi_dec_fds_ref() functions to work around a reference count issue. Remove these functions and change the implementation of usbi_poll() to take a reference to each file descriptor upon entry and drop the references when returning. If the application experiences any kind of crash, there is a problem elsewhere. Finally, make the thread executing usbi_poll() take part in the waiting. The original implementation had this thread simply waiting on a single event while separate threads waited on the HANDLEs. Now this thread will wait on MAXIMUM_WAIT_OBJECTS - 1 HANDLEs, thereby reducing the number of threads that are created. Additionally there is now only a single event object that is shared amongst all waiting threads. Signed-off-by: Chris Dickens --- libusb/io.c | 17 +- libusb/os/poll_posix.h | 3 - libusb/os/poll_windows.c | 649 ++++++++++++++++++++++++----------------------- libusb/os/poll_windows.h | 3 - libusb/version_nano.h | 2 +- 5 files changed, 336 insertions(+), 338 deletions(-) diff --git a/libusb/io.c b/libusb/io.c index 77a048f..f274a41 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -2100,7 +2100,6 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) POLL_NFDS_TYPE nfds = 0; POLL_NFDS_TYPE internal_nfds; struct pollfd *fds = NULL; - int i = -1; int timeout_ms; /* prevent attempts to recursively handle events (e.g. calling into @@ -2136,6 +2135,8 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) /* clean up removed poll fds */ cleanup_removed_pollfds(ctx); if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) { + int i = 0; + usbi_dbg("poll fds modified, reallocating"); free(ctx->pollfds); @@ -2154,9 +2155,9 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) { struct libusb_pollfd *pollfd = &ipollfd->pollfd; - i++; ctx->pollfds[i].fd = pollfd->fd; ctx->pollfds[i].events = pollfd->events; + i++; } /* reset the flag now that we have the updated list */ @@ -2169,7 +2170,6 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) } fds = ctx->pollfds; nfds = ctx->pollfds_cnt; - usbi_inc_fds_ref(fds, nfds); usbi_mutex_unlock(&ctx->event_data_lock); timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); @@ -2297,14 +2297,16 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) #endif list_for_each_entry(ipollfd, &ctx->removed_ipollfds, list, struct usbi_pollfd) { - for (i = internal_nfds ; i < nfds ; ++i) { - if (ipollfd->pollfd.fd == fds[i].fd) { + POLL_NFDS_TYPE n; + + for (n = internal_nfds ; n < nfds ; n++) { + if (ipollfd->pollfd.fd == fds[n].fd) { /* pollfd was removed between the creation of the fd * array and here. remove any triggered revent as * it is no longer relevant */ usbi_dbg("pollfd %d was removed. ignoring raised events", - fds[i].fd); - fds[i].revents = 0; + fds[n].fd); + fds[n].revents = 0; break; } } @@ -2316,7 +2318,6 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) done: usbi_end_event_handling(ctx); - usbi_dec_fds_ref(fds, nfds); return r; } diff --git a/libusb/os/poll_posix.h b/libusb/os/poll_posix.h index bc0239c..5b4b2c9 100644 --- a/libusb/os/poll_posix.h +++ b/libusb/os/poll_posix.h @@ -8,7 +8,4 @@ int usbi_pipe(int pipefd[2]); -#define usbi_inc_fds_ref(x, y) -#define usbi_dec_fds_ref(x, y) - #endif /* LIBUSB_POLL_POSIX_H */ diff --git a/libusb/os/poll_windows.c b/libusb/os/poll_windows.c index b256d69..21a1363 100644 --- a/libusb/os/poll_windows.c +++ b/libusb/os/poll_windows.c @@ -38,6 +38,8 @@ #include #include +#include +#include #include #include "libusbi.h" @@ -49,42 +51,31 @@ const struct winfd INVALID_WINFD = { -1, NULL }; // private data struct file_descriptor { enum fd_type { FD_TYPE_PIPE, FD_TYPE_TRANSFER } type; + LONG refcount; OVERLAPPED overlapped; - int refcount; }; static usbi_mutex_static_t fd_table_lock = USBI_MUTEX_INITIALIZER; -static struct file_descriptor **fd_table; -static size_t fd_count; -static size_t fd_size; -#define INC_FDS_EACH 256 +#define BITS_PER_BYTE 8 +#define BITMAP_BITS_PER_WORD (sizeof(unsigned long) * BITS_PER_BYTE) +#define FD_TABLE_INCR_SIZE 64 -static void usbi_dec_fd_table(void) -{ - fd_count--; - if (fd_count == 0) { - free(fd_table); - fd_size = 0; - fd_table = NULL; - } -} +static struct file_descriptor **fd_table; +static unsigned long *fd_table_bitmap; +static unsigned int fd_table_size; +static unsigned int fd_count; -static void smart_realloc_fd_table_space(int inc) -{ - if (fd_table == NULL || fd_count + inc > fd_size) { - struct file_descriptor **p = (struct file_descriptor **)realloc(fd_table, (fd_size + INC_FDS_EACH) * sizeof(struct file_descriptor *)); - if (p != NULL) { - memset(p + fd_size, 0, INC_FDS_EACH * sizeof(struct file_descriptor *)); - fd_size += INC_FDS_EACH; - fd_table = p; - } - } -} +#define return_with_errno(err) \ + do { \ + errno = (err); \ + return -1; \ + } while (0) -static struct file_descriptor *create_fd(enum fd_type type) +static struct file_descriptor *alloc_fd(enum fd_type type, LONG refcount) { struct file_descriptor *fd = calloc(1, sizeof(*fd)); + if (fd == NULL) return NULL; fd->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -93,14 +84,90 @@ static struct file_descriptor *create_fd(enum fd_type type) return NULL; } fd->type = type; - fd->refcount = 1; + fd->refcount = refcount; return fd; } -static void free_fd(struct file_descriptor *fd) +static struct file_descriptor *get_fd(unsigned int _fd, bool ref) +{ + struct file_descriptor *fd = NULL; + + if (_fd < fd_table_size) + fd = fd_table[_fd]; + if (fd != NULL && ref) + InterlockedIncrement(&fd->refcount); + + return fd; +} + +static void put_fd(struct file_descriptor *fd) +{ + if (InterlockedDecrement(&fd->refcount) == 0L) { + CloseHandle(fd->overlapped.hEvent); + free(fd); + } +} + +static int install_fd(struct file_descriptor *fd) +{ + unsigned int n; + + if (fd_count == fd_table_size) { + struct file_descriptor **new_table; + unsigned long* new_bitmap; + + // Need to expand the fd table and bitmap + new_table = realloc(fd_table, (fd_table_size + FD_TABLE_INCR_SIZE) * sizeof(*new_table)); + if (new_table == NULL) + return -ENOMEM; + memset(new_table + fd_table_size, 0, FD_TABLE_INCR_SIZE * sizeof(*new_table)); + fd_table = new_table; + + new_bitmap = realloc(fd_table_bitmap, (fd_table_size + FD_TABLE_INCR_SIZE) / BITS_PER_BYTE); + if (new_bitmap == NULL) + return -ENOMEM; + memset(new_bitmap + (fd_table_size / BITMAP_BITS_PER_WORD), 0, FD_TABLE_INCR_SIZE / BITS_PER_BYTE); + fd_table_bitmap = new_bitmap; + + fd_table_size += FD_TABLE_INCR_SIZE; + assert(fd_table_size < (unsigned int)INT_MAX); + } + + for (n = 0; n < fd_table_size; n += BITMAP_BITS_PER_WORD) { + unsigned int idx = n / BITMAP_BITS_PER_WORD; + unsigned long mask, pos; + + mask = ~fd_table_bitmap[idx]; + if (mask == 0UL) + continue; + + assert(_BitScanForward(&pos, mask)); + fd_table_bitmap[idx] |= 1UL << pos; + n += pos; + break; + } + + assert(n < fd_table_size); + assert(fd_table[n] == NULL); + fd_table[n] = fd; + fd_count++; + + return n; +} + +static void remove_fd(unsigned int pos) { - CloseHandle(fd->overlapped.hEvent); - free(fd); + assert(fd_table[pos] != NULL); + fd_table[pos] = NULL; + fd_table_bitmap[pos / BITMAP_BITS_PER_WORD] &= ~(1UL << (pos % BITMAP_BITS_PER_WORD)); + fd_count--; + if (fd_count == 0) { + free(fd_table); + free(fd_table_bitmap); + fd_table = NULL; + fd_table_bitmap = NULL; + fd_table_size = 0; + } } /* @@ -122,25 +189,16 @@ struct winfd usbi_create_fd(void) struct file_descriptor *fd; struct winfd wfd; - fd = create_fd(FD_TYPE_TRANSFER); + fd = alloc_fd(FD_TYPE_TRANSFER, 1); if (fd == NULL) return INVALID_WINFD; usbi_mutex_static_lock(&fd_table_lock); - - smart_realloc_fd_table_space(1); - - for (wfd.fd = 0; wfd.fd < fd_size; wfd.fd++) { - if (fd_table[wfd.fd] != NULL) - continue; - fd_table[wfd.fd] = fd; - fd_count++; - break; - } + wfd.fd = install_fd(fd); usbi_mutex_static_unlock(&fd_table_lock); - if (wfd.fd == fd_size) { - free_fd(fd); + if (wfd.fd < 0) { + put_fd(fd); return INVALID_WINFD; } @@ -149,191 +207,126 @@ struct winfd usbi_create_fd(void) return wfd; } -void usbi_inc_fds_ref(struct pollfd *fds, unsigned int nfds) -{ - int n; - usbi_mutex_static_lock(&fd_table_lock); - for (n = 0; n < nfds; ++n) { - fd_table[fds[n].fd]->refcount++; - } - usbi_mutex_static_unlock(&fd_table_lock); -} - -void usbi_dec_fds_ref(struct pollfd *fds, unsigned int nfds) -{ - int n; - struct file_descriptor *fd; - - usbi_mutex_static_lock(&fd_table_lock); - for (n = 0; n < nfds; ++n) { - fd = fd_table[fds[n].fd]; - fd->refcount--; - //FD_TYPE_PIPE map fd to two _fd - if (fd->refcount == 0 || (fd->refcount == 1 && fd->type == FD_TYPE_PIPE)) - { - if (fd->type == FD_TYPE_PIPE) { - // InternalHigh is our reference count - fd->overlapped.InternalHigh--; - if (fd->overlapped.InternalHigh == 0) - free_fd(fd); - } - else { - free_fd(fd); - } - fd_table[fds[n].fd] = NULL; - usbi_dec_fd_table(); - } - } - usbi_mutex_static_unlock(&fd_table_lock); -} - +struct wait_thread_data { + HANDLE thread; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + DWORD num_handles; + DWORD error; +}; -static int check_pollfds(struct pollfd *fds, unsigned int nfds, - HANDLE *wait_handles, DWORD *nb_wait_handles) +static DWORD WINAPI WaitThread(LPVOID lpParam) { - struct file_descriptor *fd; - unsigned int n; - int nready = 0; - - usbi_mutex_static_lock(&fd_table_lock); - - for (n = 0; n < nfds; ++n) { - fds[n].revents = 0; - - // Keep it simple - only allow either POLLIN *or* POLLOUT - assert((fds[n].events == POLLIN) || (fds[n].events == POLLOUT)); - if ((fds[n].events != POLLIN) && (fds[n].events != POLLOUT)) { - fds[n].revents = POLLNVAL; - nready++; - continue; - } - - if ((fds[n].fd >= 0) && (fds[n].fd < fd_size)) - fd = fd_table[fds[n].fd]; - else - fd = NULL; - - assert(fd != NULL); - if (fd == NULL) { - fds[n].revents = POLLNVAL; - nready++; - continue; - } - - if (HasOverlappedIoCompleted(&fd->overlapped) - && (WaitForSingleObject(fd->overlapped.hEvent, 0) == WAIT_OBJECT_0)) { - fds[n].revents = fds[n].events; - nready++; - } else if (wait_handles != NULL) { - wait_handles[*nb_wait_handles] = fd->overlapped.hEvent; - (*nb_wait_handles)++; + struct wait_thread_data *thread_data = lpParam; + HANDLE notify_event = thread_data->handles[0]; + DWORD status; + + status = WaitForMultipleObjects(thread_data->num_handles, thread_data->handles, FALSE, INFINITE); + if ((status >= WAIT_OBJECT_0) && (status < (WAIT_OBJECT_0 + thread_data->num_handles))) { + if (status > WAIT_OBJECT_0) { + // This will wake up all the other waiting threads + SetEvent(notify_event); } + thread_data->error = 0; + } else { + assert(status == WAIT_FAILED); + thread_data->error = (status == WAIT_FAILED) ? GetLastError() : ERROR_CAN_NOT_COMPLETE; } - usbi_mutex_static_unlock(&fd_table_lock); - - 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 -) +static DWORD poll_wait(const HANDLE *wait_handles, DWORD num_wait_handles, DWORD timeout) { - DWORD ret; - int i = 0; - int nThreads = 0; - struct ExThreadData *pThread; - int size; + struct wait_thread_data *thread_data; 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); - + HANDLE* handles; + int n, num_threads; + DWORD error, status; + + if (num_wait_handles <= MAXIMUM_WAIT_OBJECTS) + return WaitForMultipleObjects(num_wait_handles, wait_handles, FALSE, timeout); + + // To wait on more than MAXIMUM_WAIT_OBJECTS, each thread (including the + // current thread) will wait on an event and (MAXIMUM_WAIT_OBJECTS - 1) + // HANDLEs. The event is shared amongst all threads so that any thread + // that returns from a WaitForMultipleObjects() call will set the event + // and wake up all the other threads. notify_event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (notify_event == NULL) { - usbi_err(NULL, "Create Event failure"); + if (notify_event == NULL) return WAIT_FAILED; - } - - pThread = malloc(sizeof(struct ExThreadData) * nThreads); - if (pThread == NULL) { - usbi_err(NULL, "Out of memory"); + num_threads = 1 + (num_wait_handles - MAXIMUM_WAIT_OBJECTS - 1) / (MAXIMUM_WAIT_OBJECTS - 1); + thread_data = malloc(num_threads * sizeof(*thread_data)); + if (thread_data == NULL) { CloseHandle(notify_event); + SetLastError(ERROR_OUTOFMEMORY); 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; + handles = _alloca(MAXIMUM_WAIT_OBJECTS * sizeof(HANDLE)); + handles[0] = notify_event; + memcpy(handles + 1, wait_handles, (MAXIMUM_WAIT_OBJECTS - 1) * sizeof(HANDLE)); + wait_handles += MAXIMUM_WAIT_OBJECTS - 1; + num_wait_handles -= MAXIMUM_WAIT_OBJECTS - 1; + + for (n = 0; n < num_threads; n++) { + DWORD copy_size = MIN(num_wait_handles, MAXIMUM_WAIT_OBJECTS - 1); + + thread_data[n].handles[0] = notify_event; + memcpy(thread_data[n].handles + 1, wait_handles, copy_size * sizeof(HANDLE)); + thread_data[n].num_handles = copy_size + 1; + + // Create the thread that will wait on these HANDLEs + thread_data[n].thread = CreateThread(NULL, 0, WaitThread, &thread_data[n], 0, NULL); + if (thread_data[n].thread == NULL) { + thread_data[n].error = GetLastError(); + SetEvent(notify_event); + num_threads = n + 1; + break; + } - pThread[i].thread = CreateThread(NULL, 0, WaitThread, pThread+i, 0, NULL); + wait_handles += copy_size; + num_wait_handles -= copy_size; } - 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); + status = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, handles, FALSE, timeout); + if ((status >= WAIT_OBJECT_0) && (status < (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS))) { + if (status > WAIT_OBJECT_0) { + // Wake up all the waiting threads + SetEvent(notify_event); + status = WAIT_OBJECT_0; + } + error = 0; + } else if (status == WAIT_TIMEOUT) { + // Wake up all the waiting threads + SetEvent(notify_event); + error = 0; + } else { + assert(status == WAIT_FAILED); + error = (status == WAIT_FAILED) ? GetLastError() : ERROR_CAN_NOT_COMPLETE; + } - CloseHandle(pThread[i].wait_event[0]); + for (n = 0; n < num_threads; n++) { + if (thread_data[n].thread != NULL) { + if (WaitForSingleObject(thread_data[n].thread, INFINITE) != WAIT_OBJECT_0) + usbi_err(NULL, "WaitForSingleObject() failed: %lu", GetLastError()); + CloseHandle(thread_data[n].thread); + } + if (thread_data[n].error) { + usbi_err(NULL, "wait thread %d had error %lu\n", n, thread_data[n].error); + error = thread_data[n].error; + status = WAIT_FAILED; + } } + free(thread_data); + CloseHandle(notify_event); - free(pThread); - return ret ; + if (status == WAIT_FAILED) + SetLastError(error); + + return status; } /* @@ -343,34 +336,95 @@ static DWORD ExtendWaitForMultipleObjects( */ int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout) { - HANDLE *wait_handles; - DWORD nb_wait_handles = 0; - DWORD ret; + struct file_descriptor **fds_array; + HANDLE *handles_array; + struct file_descriptor *fd; + unsigned int n; int nready; - wait_handles = malloc(nfds * sizeof(HANDLE)); - if (!wait_handles) - { - usbi_err(NULL, "Out of memory"); - return -1; + if (nfds <= MAXIMUM_WAIT_OBJECTS) { + fds_array = _alloca(nfds * sizeof(*fds_array)); + handles_array = _alloca(nfds * sizeof(*handles_array)); + } else { + fds_array = malloc(nfds * sizeof(*fds_array)); + if (fds_array == NULL) + return_with_errno(ENOMEM); + handles_array = malloc(nfds * sizeof(*handles_array)); + if (handles_array == NULL) { + free(fds_array); + return_with_errno(ENOMEM); + } + } + + usbi_mutex_static_lock(&fd_table_lock); + for (n = 0; n < nfds; n++) { + struct pollfd *pfd = &fds[n]; + + // Keep it simple - only allow either POLLIN *or* POLLOUT + assert((pfd->events == POLLIN) || (pfd->events == POLLOUT)); + if ((pfd->events != POLLIN) && (pfd->events != POLLOUT)) { + fds_array[n] = NULL; + continue; + } + + // All file descriptors must be valid + fd = get_fd(pfd->fd, true); + assert(fd != NULL); + if (fd == NULL) { + fds_array[n] = NULL; + continue; + } + + // We hold a reference to fd for the duration of usbi_poll() + fds_array[n] = fd; + handles_array[n] = fd->overlapped.hEvent; } + usbi_mutex_static_unlock(&fd_table_lock); + + nready = 0; + while (nready == 0) { + DWORD ret; + + // Check all fds for events + for (n = 0; n < nfds; n++) { + fd = fds_array[n]; + if (fd == NULL) { + fds[n].revents = POLLNVAL; + nready++; + } else if (HasOverlappedIoCompleted(&fd->overlapped) && + (WaitForSingleObject(fd->overlapped.hEvent, 0) == WAIT_OBJECT_0)) { + fds[n].revents = fds[n].events; + nready++; + } else { + fds[n].revents = 0; + } + } + + if ((nready != 0) || (timeout == 0)) + break; - 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 = ExtendWaitForMultipleObjects(nb_wait_handles, wait_handles, - FALSE, (timeout < 0) ? INFINITE : (DWORD)timeout); - if (ret != EXT_TIMEOUT && ret <= (WAIT_OBJECT_0 + nb_wait_handles)) { - nready = check_pollfds(fds, nfds, NULL, NULL); - } else if (ret != EXT_TIMEOUT) { - if (ret == WAIT_FAILED) - usbi_err(NULL, "WaitForMultipleObjects failed: %u", (unsigned int)GetLastError()); + // Wait for any of the events to trigger + ret = poll_wait(handles_array, nfds, (timeout < 0) ? INFINITE : (DWORD)timeout); + if (ret == WAIT_TIMEOUT) { + assert(timeout > 0); + timeout = 0; + } else if (ret == WAIT_FAILED) { + usbi_err(NULL, "WaitForMultipleObjects failed: %lu", GetLastError()); + errno = EIO; nready = -1; } } - free(wait_handles); + for (n = 0; n < nfds; n++) { + if (fds_array[n] != NULL) + put_fd(fds_array[n]); + } + + if (nfds > MAXIMUM_WAIT_OBJECTS) { + free(handles_array); + free(fds_array); + } + return nready; } @@ -381,37 +435,18 @@ int usbi_close(int _fd) { struct file_descriptor *fd; - if (_fd < 0 || _fd >= fd_size) - goto err_badfd; - usbi_mutex_static_lock(&fd_table_lock); - fd = fd_table[_fd]; - fd->refcount--; - //FD_TYPE_PIPE map fd to two _fd - if(fd->refcount==0 || (fd->refcount == 1 && fd->type == FD_TYPE_PIPE)) - { fd_table[_fd] = NULL; - usbi_dec_fd_table(); - - if (fd->type == FD_TYPE_PIPE) { - // InternalHigh is our reference count - fd->overlapped.InternalHigh--; - if (fd->overlapped.InternalHigh == 0) - free_fd(fd); - } - else { - free_fd(fd); - } - } + fd = get_fd(_fd, false); + if (fd != NULL) + remove_fd(_fd); usbi_mutex_static_unlock(&fd_table_lock); if (fd == NULL) - goto err_badfd; + return_with_errno(EBADF); - return 0; + put_fd(fd); -err_badfd: - errno = EBADF; - return -1; + return 0; } /* @@ -423,51 +458,33 @@ err_badfd: int usbi_pipe(int filedes[2]) { struct file_descriptor *fd; - int r_fd = -1, w_fd = -1; - int i; + int r_fd, w_fd; + int error = 0; - fd = create_fd(FD_TYPE_PIPE); - if (fd == NULL) { - errno = ENOMEM; - return -1; - } + fd = alloc_fd(FD_TYPE_PIPE, 2); + if (fd == NULL) + return_with_errno(ENOMEM); - // Use InternalHigh as a reference count fd->overlapped.Internal = STATUS_PENDING; - fd->overlapped.InternalHigh = 2; usbi_mutex_static_lock(&fd_table_lock); - do { - smart_realloc_fd_table_space(2); - - for (i = 0; i < fd_size; i++) { - if (fd_table[i] != NULL) - continue; - if (r_fd == -1) { - r_fd = i; - } else if (w_fd == -1) { - w_fd = i; - break; - } + r_fd = install_fd(fd); + if (r_fd >= 0) { + w_fd = install_fd(fd); + if (w_fd < 0) { + remove_fd(r_fd); + error = w_fd; } - - if (i == fd_size) - break; - - fd_table[r_fd] = fd; - fd_table[w_fd] = fd; - - fd->refcount++; //this fd reference twice for r and w. - - fd_count += 2; - - } while (0); + } else { + error = r_fd; + w_fd = -1; // Keep compiler happy + } usbi_mutex_static_unlock(&fd_table_lock); - if (i == fd_size) { - free_fd(fd); - errno = EMFILE; - return -1; + if (error) { + CloseHandle(fd->overlapped.hEvent); + free(fd); + return_with_errno(error); } filedes[0] = r_fd; @@ -479,75 +496,61 @@ int usbi_pipe(int filedes[2]) /* * synchronous write for fake "pipe" signaling */ -ssize_t usbi_write(int fd, const void *buf, size_t count) +ssize_t usbi_write(int _fd, const void *buf, size_t count) { - int error = EBADF; + struct file_descriptor *fd; UNUSED(buf); - if (fd < 0 || fd >= fd_size) - goto err_out; - if (count != sizeof(unsigned char)) { usbi_err(NULL, "this function should only used for signaling"); - error = EINVAL; - goto err_out; + return_with_errno(EINVAL); } usbi_mutex_static_lock(&fd_table_lock); - if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) { - assert(fd_table[fd]->overlapped.Internal == STATUS_PENDING); - assert(fd_table[fd]->overlapped.InternalHigh == 2); - fd_table[fd]->overlapped.Internal = STATUS_WAIT_0; - SetEvent(fd_table[fd]->overlapped.hEvent); - error = 0; + fd = get_fd(_fd, false); + if (fd && fd->type == FD_TYPE_PIPE) { + assert(fd->overlapped.Internal == STATUS_PENDING); + fd->overlapped.Internal = STATUS_WAIT_0; + SetEvent(fd->overlapped.hEvent); + } else { + fd = NULL; } usbi_mutex_static_unlock(&fd_table_lock); - if (error) - goto err_out; + if (fd == NULL) + return_with_errno(EBADF); return sizeof(unsigned char); - -err_out: - errno = error; - return -1; } /* * synchronous read for fake "pipe" signaling */ -ssize_t usbi_read(int fd, void *buf, size_t count) +ssize_t usbi_read(int _fd, void *buf, size_t count) { - int error = EBADF; + struct file_descriptor *fd; UNUSED(buf); - if (fd < 0 || fd >= fd_size) - goto err_out; - if (count != sizeof(unsigned char)) { usbi_err(NULL, "this function should only used for signaling"); - error = EINVAL; - goto err_out; + return_with_errno(EINVAL); } usbi_mutex_static_lock(&fd_table_lock); - if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) { - assert(fd_table[fd]->overlapped.Internal == STATUS_WAIT_0); - assert(fd_table[fd]->overlapped.InternalHigh == 2); - fd_table[fd]->overlapped.Internal = STATUS_PENDING; - ResetEvent(fd_table[fd]->overlapped.hEvent); - error = 0; + fd = get_fd(_fd, false); + if (fd && fd->type == FD_TYPE_PIPE) { + assert(fd->overlapped.Internal == STATUS_WAIT_0); + fd->overlapped.Internal = STATUS_PENDING; + ResetEvent(fd->overlapped.hEvent); + } else { + fd = NULL; } usbi_mutex_static_unlock(&fd_table_lock); - if (error) - goto err_out; + if (fd == NULL) + return_with_errno(EBADF); return sizeof(unsigned char); - -err_out: - errno = error; - return -1; } diff --git a/libusb/os/poll_windows.h b/libusb/os/poll_windows.h index 986b9c9..6c51919 100644 --- a/libusb/os/poll_windows.h +++ b/libusb/os/poll_windows.h @@ -65,9 +65,6 @@ ssize_t usbi_write(int fd, const void *buf, size_t count); ssize_t usbi_read(int fd, void *buf, size_t count); int usbi_close(int fd); -void usbi_inc_fds_ref(struct pollfd *fds, unsigned int nfds); -void usbi_dec_fds_ref(struct pollfd *fds, unsigned int nfds); - /* * Timeval operations */ diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 60e92e3..22e41cb 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11436 +#define LIBUSB_NANO 11437 -- 2.7.4