diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-06-24 23:01:51 -0500 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-06-26 22:42:59 -0500 |
commit | 1df713d622ab4f0b03aad72d903ac7beb8fb3b90 (patch) | |
tree | 60d12ab36c2a75dbb80104d956712e6f47c72990 /libusb/io.c | |
parent | 9818151c60a85aea6af24cb0996a92c3726c9864 (diff) | |
download | libusb-1df713d622ab4f0b03aad72d903ac7beb8fb3b90.tar.gz |
Introduce contexts to the API
Suggested by David Zeuthen. This allows multiple libraries in the same
process to independently use libusb without interfering.
Diffstat (limited to 'libusb/io.c')
-rw-r--r-- | libusb/io.c | 324 |
1 files changed, 169 insertions, 155 deletions
diff --git a/libusb/io.c b/libusb/io.c index 5f3d617..f2de833 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -32,32 +32,6 @@ #include "libusbi.h" -/* this is a list of in-flight transfer handles, sorted by timeout expiration. - * URBs to timeout the soonest are placed at the beginning of the list, URBs - * that will time out later are placed after, and urbs with infinite timeout - * are always placed at the very end. */ -static struct list_head flying_transfers; -static pthread_mutex_t flying_transfers_lock = PTHREAD_MUTEX_INITIALIZER; - -/* list of poll fd's */ -static struct list_head pollfds; -static pthread_mutex_t pollfds_lock = PTHREAD_MUTEX_INITIALIZER; - -/* user callbacks for pollfd changes */ -static libusb_pollfd_added_cb fd_added_cb = NULL; -static libusb_pollfd_removed_cb fd_removed_cb = NULL; - -/* this lock ensures that only one thread is handling events at any one time */ -static pthread_mutex_t events_lock = PTHREAD_MUTEX_INITIALIZER; - -/* used to see if there is an active thread doing event handling */ -static int event_handler_active = 0; - -/* used to wait for event completion in threads other than the one that is - * event handling */ -static pthread_mutex_t event_waiters_lock = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t event_waiters_cond = PTHREAD_COND_INITIALIZER; - /** * \page io Synchronous and asynchronous device I/O * @@ -575,7 +549,7 @@ if (r == 0 && actual_length == sizeof(data)) { // maybe fire off some initial async I/O while (user_has_not_requested_exit) - libusb_handle_events(); + libusb_handle_events(ctx); // clean up and exit \endcode @@ -606,15 +580,15 @@ while (user_has_not_requested_exit) \code // initialise libusb -libusb_get_pollfds() +libusb_get_pollfds(ctx) while (user has not requested application exit) { - libusb_get_next_timeout(); + libusb_get_next_timeout(ctx); select(on libusb file descriptors plus any other event sources of interest, using a timeout no larger than the value libusb just suggested) if (select() indicated activity on libusb file descriptors) - libusb_handle_events_timeout(0); + libusb_handle_events_timeout(ctx, 0); if (time has elapsed to or beyond the libusb timeout) - libusb_handle_events_timeout(0); + libusb_handle_events_timeout(ctx, 0); } // clean up and exit @@ -684,7 +658,7 @@ void myfunc() { while (!completed) { poll(libusb file descriptors, 120*1000); if (poll indicates activity) - libusb_handle_events_timeout(0); + libusb_handle_events_timeout(ctx, 0); } printf("completed!"); // other code here @@ -738,13 +712,13 @@ void myfunc() { * Although the events lock is a critical part of the solution, it is not * enough on it's own. You might wonder if the following is sufficient... \code - libusb_lock_events(); + libusb_lock_events(ctx); while (!completed) { poll(libusb file descriptors, 120*1000); if (poll indicates activity) - libusb_handle_events_timeout(0); + libusb_handle_events_timeout(ctx, 0); } - libusb_lock_events(); + libusb_lock_events(ctx); \endcode * ...and the answer is that it is not. This is because the transfer in the * code shown above may take a long time (say 30 seconds) to complete, and @@ -775,33 +749,33 @@ void myfunc() { * This looks like the following, as pseudo-code: \code retry: -if (libusb_try_lock_events() == 0) { +if (libusb_try_lock_events(ctx) == 0) { // we obtained the event lock: do our own event handling - libusb_lock_events(); + libusb_lock_events(ctx); while (!completed) { poll(libusb file descriptors, 120*1000); if (poll indicates activity) - libusb_handle_events_locked(0); + libusb_handle_events_locked(ctx, 0); } - libusb_unlock_events(); + libusb_unlock_events(ctx); } else { // another thread is doing event handling. wait for it to signal us that // an event has completed - libusb_lock_event_waiters(); + libusb_lock_event_waiters(ctx); while (!completed) { // now that we have the event waiters lock, double check that another // thread is still handling events for us. (it may have ceased handling // events in the time it took us to reach this point) - if (!libusb_event_handler_active()) { + if (!libusb_event_handler_active(ctx)) { // whoever was handling events is no longer doing so, try again - libusb_unlock_event_waiters(); + libusb_unlock_event_waiters(ctx); goto retry; } - libusb_wait_for_event(); + libusb_wait_for_event(ctx); } - libusb_unlock_event_waiters(); + libusb_unlock_event_waiters(ctx); } printf("completed!\n"); \endcode @@ -858,12 +832,15 @@ printf("completed!\n"); * fall back to the "event waiters" mechanism detailed above. */ -void usbi_io_init() +void usbi_io_init(struct libusb_context *ctx) { - list_init(&flying_transfers); - list_init(&pollfds); - fd_added_cb = NULL; - fd_removed_cb = NULL; + pthread_mutex_init(&ctx->flying_transfers_lock, NULL); + pthread_mutex_init(&ctx->pollfds_lock, NULL); + pthread_mutex_init(&ctx->events_lock, NULL); + pthread_mutex_init(&ctx->event_waiters_lock, NULL); + pthread_cond_init(&ctx->event_waiters_cond, NULL); + list_init(&ctx->flying_transfers); + list_init(&ctx->pollfds); } static int calculate_timeout(struct usbi_transfer *transfer) @@ -878,7 +855,8 @@ static int calculate_timeout(struct usbi_transfer *transfer) r = clock_gettime(CLOCK_MONOTONIC, ¤t_time); if (r < 0) { - usbi_err("failed to read monotonic clock, errno=%d", errno); + usbi_err(ITRANSFER_CTX(transfer), + "failed to read monotonic clock, errno=%d", errno); return r; } @@ -898,23 +876,24 @@ static void add_to_flying_list(struct usbi_transfer *transfer) { struct usbi_transfer *cur; struct timeval *timeout = &transfer->timeout; + struct libusb_context *ctx = ITRANSFER_CTX(transfer); - pthread_mutex_lock(&flying_transfers_lock); + pthread_mutex_lock(&ctx->flying_transfers_lock); /* if we have no other flying transfers, start the list with this one */ - if (list_empty(&flying_transfers)) { - list_add(&transfer->list, &flying_transfers); + if (list_empty(&ctx->flying_transfers)) { + list_add(&transfer->list, &ctx->flying_transfers); goto out; } /* if we have infinite timeout, append to end of list */ if (!timerisset(timeout)) { - list_add_tail(&transfer->list, &flying_transfers); + list_add_tail(&transfer->list, &ctx->flying_transfers); goto out; } /* otherwise, find appropriate place in list */ - list_for_each_entry(cur, &flying_transfers, list) { + list_for_each_entry(cur, &ctx->flying_transfers, list) { /* find first timeout that occurs after the transfer in question */ struct timeval *cur_tv = &cur->timeout; @@ -927,9 +906,9 @@ static void add_to_flying_list(struct usbi_transfer *transfer) } /* otherwise we need to be inserted at the end */ - list_add_tail(&transfer->list, &flying_transfers); + list_add_tail(&transfer->list, &ctx->flying_transfers); out: - pthread_mutex_unlock(&flying_transfers_lock); + pthread_mutex_unlock(&ctx->flying_transfers_lock); } /** \ingroup asyncio @@ -1025,9 +1004,9 @@ API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer) add_to_flying_list(itransfer); r = usbi_backend->submit_transfer(itransfer); if (r) { - pthread_mutex_lock(&flying_transfers_lock); + pthread_mutex_lock(&TRANSFER_CTX(transfer)->flying_transfers_lock); list_del(&itransfer->list); - pthread_mutex_unlock(&flying_transfers_lock); + pthread_mutex_unlock(&TRANSFER_CTX(transfer)->flying_transfers_lock); } return r; @@ -1056,7 +1035,8 @@ API_EXPORTED int libusb_cancel_transfer(struct libusb_transfer *transfer) usbi_dbg(""); r = usbi_backend->cancel_transfer(itransfer); if (r < 0) - usbi_err("cancel transfer failed error %d", r); + usbi_err(TRANSFER_CTX(transfer), + "cancel transfer failed error %d", r); return r; } @@ -1070,11 +1050,12 @@ void usbi_handle_transfer_completion(struct usbi_transfer *itransfer, { struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = TRANSFER_CTX(transfer); uint8_t flags; - pthread_mutex_lock(&flying_transfers_lock); + pthread_mutex_lock(&ctx->flying_transfers_lock); list_del(&itransfer->list); - pthread_mutex_unlock(&flying_transfers_lock); + pthread_mutex_unlock(&ctx->flying_transfers_lock); if (status == LIBUSB_TRANSFER_COMPLETED && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { @@ -1096,9 +1077,9 @@ void usbi_handle_transfer_completion(struct usbi_transfer *itransfer, * this point. */ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) libusb_free_transfer(transfer); - pthread_mutex_lock(&event_waiters_lock); - pthread_cond_broadcast(&event_waiters_cond); - pthread_mutex_unlock(&event_waiters_lock); + pthread_mutex_lock(&ctx->event_waiters_lock); + pthread_cond_broadcast(&ctx->event_waiters_cond); + pthread_mutex_unlock(&ctx->event_waiters_lock); } /* Similar to usbi_handle_transfer_completion() but exclusively for transfers @@ -1132,17 +1113,21 @@ void usbi_handle_transfer_cancellation(struct usbi_transfer *transfer) * If you are no longer handling events, you must call libusb_unlock_events() * as soon as possible. * + * \param ctx the context to operate on, or NULL for the default context * \returns 0 if the lock was obtained successfully * \returns 1 if the lock was not obtained (i.e. another thread holds the lock) * \see \ref mtasync */ -API_EXPORTED int libusb_try_lock_events(void) +API_EXPORTED int libusb_try_lock_events(libusb_context *ctx) { - int r = pthread_mutex_trylock(&events_lock); + int r; + USBI_GET_CONTEXT(ctx); + + r = pthread_mutex_trylock(&ctx->events_lock); if (r) return 1; - event_handler_active = 1; + ctx->event_handler_active = 1; return 0; } @@ -1161,12 +1146,14 @@ API_EXPORTED int libusb_try_lock_events(void) * If you are no longer handling events, you must call libusb_unlock_events() * as soon as possible. * + * \param ctx the context to operate on, or NULL for the default context * \see \ref mtasync */ -API_EXPORTED void libusb_lock_events(void) +API_EXPORTED void libusb_lock_events(libusb_context *ctx) { - pthread_mutex_lock(&events_lock); - event_handler_active = 1; + USBI_GET_CONTEXT(ctx); + pthread_mutex_lock(&ctx->events_lock); + ctx->event_handler_active = 1; } /** \ingroup poll @@ -1174,29 +1161,33 @@ API_EXPORTED void libusb_lock_events(void) * libusb_lock_events(). Releasing this lock will wake up any threads blocked * on libusb_wait_for_event(). * + * \param ctx the context to operate on, or NULL for the default context * \see \ref mtasync */ -API_EXPORTED void libusb_unlock_events(void) +API_EXPORTED void libusb_unlock_events(libusb_context *ctx) { - event_handler_active = 0; - pthread_mutex_unlock(&events_lock); + USBI_GET_CONTEXT(ctx); + ctx->event_handler_active = 0; + pthread_mutex_unlock(&ctx->events_lock); - pthread_mutex_lock(&event_waiters_lock); - pthread_cond_broadcast(&event_waiters_cond); - pthread_mutex_unlock(&event_waiters_lock); + pthread_mutex_lock(&ctx->event_waiters_lock); + pthread_cond_broadcast(&ctx->event_waiters_cond); + pthread_mutex_unlock(&ctx->event_waiters_lock); } /** \ingroup poll * Determine if an active thread is handling events (i.e. if anyone is holding * the event handling lock). * + * \param ctx the context to operate on, or NULL for the default context * \returns 1 if a thread is handling events * \returns 0 if there are no threads currently handling events * \see \ref mtasync */ -API_EXPORTED int libusb_event_handler_active(void) +API_EXPORTED int libusb_event_handler_active(libusb_context *ctx) { - return event_handler_active; + USBI_GET_CONTEXT(ctx); + return ctx->event_handler_active; } /** \ingroup poll @@ -1215,20 +1206,24 @@ API_EXPORTED int libusb_event_handler_active(void) * libusb_handle_events()) then you do not need to be concerned with this * locking. * + * \param ctx the context to operate on, or NULL for the default context * \see \ref mtasync */ -API_EXPORTED void libusb_lock_event_waiters(void) +API_EXPORTED void libusb_lock_event_waiters(libusb_context *ctx) { - pthread_mutex_lock(&event_waiters_lock); + USBI_GET_CONTEXT(ctx); + pthread_mutex_lock(&ctx->event_waiters_lock); } /** \ingroup poll * Release the event waiters lock. + * \param ctx the context to operate on, or NULL for the default context * \see \ref mtasync */ -API_EXPORTED void libusb_unlock_event_waiters(void) +API_EXPORTED void libusb_unlock_event_waiters(libusb_context *ctx) { - pthread_mutex_unlock(&event_waiters_lock); + USBI_GET_CONTEXT(ctx); + pthread_mutex_unlock(&ctx->event_waiters_lock); } /** \ingroup poll @@ -1249,25 +1244,27 @@ API_EXPORTED void libusb_unlock_event_waiters(void) * This function releases the event waiters lock before putting your thread * to sleep, and reacquires the lock as it is being woken up. * + * \param ctx the context to operate on, or NULL for the default context * \param tv maximum timeout for this blocking function. A NULL value * indicates unlimited timeout. * \returns 0 after a transfer completes or another thread stops event handling * \returns 1 if the timeout expired * \see \ref mtasync */ -API_EXPORTED int libusb_wait_for_event(struct timeval *tv) +API_EXPORTED int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) { struct timespec timeout; int r; + USBI_GET_CONTEXT(ctx); if (tv == NULL) { - pthread_cond_wait(&event_waiters_cond, &event_waiters_lock); + pthread_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock); return 0; } r = clock_gettime(CLOCK_REALTIME, &timeout); if (r < 0) { - usbi_err("failed to read realtime clock, error %d", errno); + usbi_err(ctx, "failed to read realtime clock, error %d", errno); return LIBUSB_ERROR_OTHER; } @@ -1278,8 +1275,8 @@ API_EXPORTED int libusb_wait_for_event(struct timeval *tv) timeout.tv_sec++; } - r = pthread_cond_timedwait(&event_waiters_cond, &event_waiters_lock, - &timeout); + r = pthread_cond_timedwait(&ctx->event_waiters_cond, + &ctx->event_waiters_lock, &timeout); return (r == ETIMEDOUT); } @@ -1292,18 +1289,20 @@ static void handle_timeout(struct usbi_transfer *itransfer) itransfer->flags |= USBI_TRANSFER_TIMED_OUT; r = libusb_cancel_transfer(transfer); if (r < 0) - usbi_warn("async cancel failed %d errno=%d", r, errno); + usbi_warn(TRANSFER_CTX(transfer), + "async cancel failed %d errno=%d", r, errno); } -static int handle_timeouts(void) +static int handle_timeouts(struct libusb_context *ctx) { struct timespec systime_ts; struct timeval systime; struct usbi_transfer *transfer; int r = 0; - pthread_mutex_lock(&flying_transfers_lock); - if (list_empty(&flying_transfers)) + USBI_GET_CONTEXT(ctx); + pthread_mutex_lock(&ctx->flying_transfers_lock); + if (list_empty(&ctx->flying_transfers)) goto out; /* get current time */ @@ -1315,7 +1314,7 @@ static int handle_timeouts(void) /* iterate through flying transfers list, finding all transfers that * have expired timeouts */ - list_for_each_entry(transfer, &flying_transfers, list) { + list_for_each_entry(transfer, &ctx->flying_transfers, list) { struct timeval *cur_tv = &transfer->timeout; /* if we've reached transfers of infinite timeout, we're all done */ @@ -1337,13 +1336,13 @@ static int handle_timeouts(void) } out: - pthread_mutex_unlock(&flying_transfers_lock); + pthread_mutex_unlock(&ctx->flying_transfers_lock); return r; } /* do the actual event handling. assumes that no other thread is concurrently * doing the same thing. */ -static int handle_events(struct timeval *tv) +static int handle_events(struct libusb_context *ctx, struct timeval *tv) { int r; struct usbi_pollfd *ipollfd; @@ -1352,8 +1351,8 @@ static int handle_events(struct timeval *tv) int i = -1; int timeout_ms; - pthread_mutex_lock(&pollfds_lock); - list_for_each_entry(ipollfd, &pollfds, list) + pthread_mutex_lock(&ctx->pollfds_lock); + list_for_each_entry(ipollfd, &ctx->pollfds, list) nfds++; /* TODO: malloc when number of fd's changes, not on every poll */ @@ -1361,7 +1360,7 @@ static int handle_events(struct timeval *tv) if (!fds) return LIBUSB_ERROR_NO_MEM; - list_for_each_entry(ipollfd, &pollfds, list) { + list_for_each_entry(ipollfd, &ctx->pollfds, list) { struct libusb_pollfd *pollfd = &ipollfd->pollfd; int fd = pollfd->fd; i++; @@ -1369,7 +1368,7 @@ static int handle_events(struct timeval *tv) fds[i].events = pollfd->events; fds[i].revents = 0; } - pthread_mutex_unlock(&pollfds_lock); + pthread_mutex_unlock(&ctx->pollfds_lock); timeout_ms = (tv->tv_sec * 1000) + (tv->tv_usec / 1000); @@ -1382,19 +1381,19 @@ static int handle_events(struct timeval *tv) usbi_dbg("poll() returned %d", r); if (r == 0) { free(fds); - return handle_timeouts(); + return handle_timeouts(ctx); } else if (r == -1 && errno == EINTR) { free(fds); return LIBUSB_ERROR_INTERRUPTED; } else if (r < 0) { free(fds); - usbi_err("poll failed %d err=%d\n", r, errno); + usbi_err(ctx, "poll failed %d err=%d\n", r, errno); return LIBUSB_ERROR_IO; } - r = usbi_backend->handle_events(fds, nfds, r); + r = usbi_backend->handle_events(ctx, fds, nfds, r); if (r) - usbi_err("backend handle_events failed with error %d", r); + usbi_err(ctx, "backend handle_events failed with error %d", r); free(fds); return r; @@ -1406,10 +1405,11 @@ static int handle_events(struct timeval *tv) * returns 1 if there is an already-expired timeout, otherwise returns 0 * and populates out */ -static int get_next_timeout(struct timeval *tv, struct timeval *out) +static int get_next_timeout(libusb_context *ctx, struct timeval *tv, + struct timeval *out) { struct timeval timeout; - int r = libusb_get_next_timeout(&timeout); + int r = libusb_get_next_timeout(ctx, &timeout); if (r) { /* timeout already expired? */ if (!timerisset(&timeout)) @@ -1440,49 +1440,52 @@ static int get_next_timeout(struct timeval *tv, struct timeval *out) * timeout. If an event arrives or a signal is raised, this function will * return early. * + * \param ctx the context to operate on, or NULL for the default context * \param tv the maximum time to block waiting for events, or zero for * non-blocking mode * \returns 0 on success, or a LIBUSB_ERROR code on failure */ -API_EXPORTED int libusb_handle_events_timeout(struct timeval *tv) +API_EXPORTED int libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv) { int r; struct timeval poll_timeout; - r = get_next_timeout(tv, &poll_timeout); + USBI_GET_CONTEXT(ctx); + r = get_next_timeout(ctx, tv, &poll_timeout); if (r) { /* timeout already expired */ - return handle_timeouts(); + return handle_timeouts(ctx); } retry: - if (libusb_try_lock_events() == 0) { + if (libusb_try_lock_events(ctx) == 0) { /* we obtained the event lock: do our own event handling */ - r = handle_events(&poll_timeout); - libusb_unlock_events(); + r = handle_events(ctx, &poll_timeout); + libusb_unlock_events(ctx); return r; } /* another thread is doing event handling. wait for pthread events that * notify event completion. */ - libusb_lock_event_waiters(); + libusb_lock_event_waiters(ctx); - if (!libusb_event_handler_active()) { + if (!libusb_event_handler_active(ctx)) { /* we hit a race: whoever was event handling earlier finished in the * time it took us to reach this point. try the cycle again. */ - libusb_unlock_event_waiters(); + libusb_unlock_event_waiters(ctx); usbi_dbg("event handler was active but went away, retrying"); goto retry; } usbi_dbg("another thread is doing event handling"); - r = libusb_wait_for_event(&poll_timeout); - libusb_unlock_event_waiters(); + r = libusb_wait_for_event(ctx, &poll_timeout); + libusb_unlock_event_waiters(ctx); if (r < 0) return r; else if (r == 1) - return handle_timeouts(); + return handle_timeouts(ctx); else return 0; } @@ -1494,14 +1497,15 @@ retry: * function is blocking or non-blocking, or the maximum timeout, use * libusb_handle_events_timeout() instead. * + * \param ctx the context to operate on, or NULL for the default context * \returns 0 on success, or a LIBUSB_ERROR code on failure */ -API_EXPORTED int libusb_handle_events(void) +API_EXPORTED int libusb_handle_events(libusb_context *ctx) { struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; - return libusb_handle_events_timeout(&tv); + return libusb_handle_events_timeout(ctx, &tv); } /** \ingroup poll @@ -1515,23 +1519,26 @@ API_EXPORTED int libusb_handle_events(void) * You detect events on libusb's descriptors, so you then call this function * with a zero timeout value (while still holding the event lock). * + * \param ctx the context to operate on, or NULL for the default context * \param tv the maximum time to block waiting for events, or zero for * non-blocking mode * \returns 0 on success, or a LIBUSB_ERROR code on failure * \see \ref mtasync */ -API_EXPORTED int libusb_handle_events_locked(struct timeval *tv) +API_EXPORTED int libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv) { int r; struct timeval poll_timeout; - r = get_next_timeout(tv, &poll_timeout); + USBI_GET_CONTEXT(ctx); + r = get_next_timeout(ctx, tv, &poll_timeout); if (r) { /* timeout already expired */ - return handle_timeouts(); + return handle_timeouts(ctx); } - return handle_events(&poll_timeout); + return handle_events(ctx, &poll_timeout); } /** \ingroup poll @@ -1553,12 +1560,14 @@ API_EXPORTED int libusb_handle_events_locked(struct timeval *tv) * so you should call libusb_handle_events_timeout() or similar immediately. * A return code of 0 indicates that there are no pending timeouts. * + * \param ctx the context to operate on, or NULL for the default context * \param tv output location for a relative time against the current * clock in which libusb must be called into in order to process timeout events * \returns 0 if there are no pending timeouts, 1 if a timeout was returned, * or LIBUSB_ERROR_OTHER on failure */ -API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) +API_EXPORTED int libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv) { struct usbi_transfer *transfer; struct timespec cur_ts; @@ -1567,21 +1576,22 @@ API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) int r; int found = 0; - pthread_mutex_lock(&flying_transfers_lock); - if (list_empty(&flying_transfers)) { - pthread_mutex_unlock(&flying_transfers_lock); + USBI_GET_CONTEXT(ctx); + pthread_mutex_lock(&ctx->flying_transfers_lock); + if (list_empty(&ctx->flying_transfers)) { + pthread_mutex_unlock(&ctx->flying_transfers_lock); usbi_dbg("no URBs, no timeout!"); return 0; } /* find next transfer which hasn't already been processed as timed out */ - list_for_each_entry(transfer, &flying_transfers, list) { + list_for_each_entry(transfer, &ctx->flying_transfers, list) { if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) { found = 1; break; } } - pthread_mutex_unlock(&flying_transfers_lock); + pthread_mutex_unlock(&ctx->flying_transfers_lock); if (!found) { usbi_dbg("all URBs have already been processed for timeouts"); @@ -1598,7 +1608,7 @@ API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) r = clock_gettime(CLOCK_MONOTONIC, &cur_ts); if (r < 0) { - usbi_err("failed to read monotonic clock, errno=%d", errno); + usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno); return LIBUSB_ERROR_OTHER; } TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); @@ -1621,20 +1631,22 @@ API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) * * To remove notifiers, pass NULL values for the function pointers. * + * \param ctx the context to operate on, or NULL for the default context * \param added_cb pointer to function for addition notifications * \param removed_cb pointer to function for removal notifications */ -API_EXPORTED void libusb_set_pollfd_notifiers(libusb_pollfd_added_cb added_cb, - libusb_pollfd_removed_cb removed_cb) +API_EXPORTED void libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb) { - fd_added_cb = added_cb; - fd_removed_cb = removed_cb; + USBI_GET_CONTEXT(ctx); + ctx->fd_added_cb = added_cb; + ctx->fd_removed_cb = removed_cb; } /* Add a file descriptor to the list of file descriptors to be monitored. * events should be specified as a bitmask of events passed to poll(), e.g. * POLLIN and/or POLLOUT. */ -int usbi_add_pollfd(int fd, short events) +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events) { struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd)); if (!ipollfd) @@ -1643,24 +1655,24 @@ int usbi_add_pollfd(int fd, short events) usbi_dbg("add fd %d events %d", fd, events); ipollfd->pollfd.fd = fd; ipollfd->pollfd.events = events; - pthread_mutex_lock(&pollfds_lock); - list_add(&ipollfd->list, &pollfds); - pthread_mutex_unlock(&pollfds_lock); + pthread_mutex_lock(&ctx->pollfds_lock); + list_add(&ipollfd->list, &ctx->pollfds); + pthread_mutex_unlock(&ctx->pollfds_lock); - if (fd_added_cb) - fd_added_cb(fd, events); + if (ctx->fd_added_cb) + ctx->fd_added_cb(fd, events); return 0; } /* Remove a file descriptor from the list of file descriptors to be polled. */ -void usbi_remove_pollfd(int fd) +void usbi_remove_pollfd(struct libusb_context *ctx, int fd) { struct usbi_pollfd *ipollfd; int found = 0; usbi_dbg("remove fd %d", fd); - pthread_mutex_lock(&pollfds_lock); - list_for_each_entry(ipollfd, &pollfds, list) + pthread_mutex_lock(&ctx->pollfds_lock); + list_for_each_entry(ipollfd, &ctx->pollfds, list) if (ipollfd->pollfd.fd == fd) { found = 1; break; @@ -1668,15 +1680,15 @@ void usbi_remove_pollfd(int fd) if (!found) { usbi_dbg("couldn't find fd %d to remove", fd); - pthread_mutex_unlock(&pollfds_lock); + pthread_mutex_unlock(&ctx->pollfds_lock); return; } list_del(&ipollfd->list); - pthread_mutex_unlock(&pollfds_lock); + pthread_mutex_unlock(&ctx->pollfds_lock); free(ipollfd); - if (fd_removed_cb) - fd_removed_cb(fd); + if (ctx->fd_removed_cb) + ctx->fd_removed_cb(fd); } /** \ingroup poll @@ -1686,30 +1698,32 @@ void usbi_remove_pollfd(int fd) * The returned list is NULL-terminated and should be freed with free() when * done. The actual list contents must not be touched. * + * \param ctx the context to operate on, or NULL for the default context * \returns a NULL-terminated list of libusb_pollfd structures, or NULL on * error */ -API_EXPORTED const struct libusb_pollfd **libusb_get_pollfds(void) +API_EXPORTED const struct libusb_pollfd **libusb_get_pollfds( + libusb_context *ctx) { struct libusb_pollfd **ret = NULL; struct usbi_pollfd *ipollfd; size_t i = 0; size_t cnt = 0; - pthread_mutex_lock(&pollfds_lock); - list_for_each_entry(ipollfd, &pollfds, list) + pthread_mutex_lock(&ctx->pollfds_lock); + list_for_each_entry(ipollfd, &ctx->pollfds, list) cnt++; ret = calloc(cnt + 1, sizeof(struct libusb_pollfd *)); if (!ret) goto out; - list_for_each_entry(ipollfd, &pollfds, list) + list_for_each_entry(ipollfd, &ctx->pollfds, list) ret[i++] = (struct libusb_pollfd *) ipollfd; ret[cnt] = NULL; out: - pthread_mutex_unlock(&pollfds_lock); + pthread_mutex_unlock(&ctx->pollfds_lock); return (const struct libusb_pollfd **) ret; } @@ -1738,14 +1752,14 @@ void usbi_handle_disconnect(struct libusb_device_handle *handle) */ while (1) { - pthread_mutex_lock(&flying_transfers_lock); + pthread_mutex_lock(&HANDLE_CTX(handle)->flying_transfers_lock); to_cancel = NULL; - list_for_each_entry(cur, &flying_transfers, list) + list_for_each_entry(cur, &HANDLE_CTX(handle)->flying_transfers, list) if (__USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == handle) { to_cancel = cur; break; } - pthread_mutex_unlock(&flying_transfers_lock); + pthread_mutex_unlock(&HANDLE_CTX(handle)->flying_transfers_lock); if (!to_cancel) break; |