?? io.c
字號:
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 */ if (!timerisset(cur_tv)) goto out; /* ignore timeouts we've already handled */ if (transfer->flags & USBI_TRANSFER_TIMED_OUT) continue; /* if transfer has non-expired timeout, nothing more to do */ if ((cur_tv->tv_sec > systime.tv_sec) || (cur_tv->tv_sec == systime.tv_sec && cur_tv->tv_usec > systime.tv_usec)) goto out; /* otherwise, we've got an expired timeout to handle */ handle_timeout(transfer); }out: 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 libusb_context *ctx, struct timeval *tv){ int r; struct usbi_pollfd *ipollfd; nfds_t nfds = 0; struct pollfd *fds; int i = -1; int timeout_ms; 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 */ fds = malloc(sizeof(*fds) * nfds); if (!fds) return LIBUSB_ERROR_NO_MEM; list_for_each_entry(ipollfd, &ctx->pollfds, list) { struct libusb_pollfd *pollfd = &ipollfd->pollfd; int fd = pollfd->fd; i++; fds[i].fd = fd; fds[i].events = pollfd->events; fds[i].revents = 0; } pthread_mutex_unlock(&ctx->pollfds_lock); timeout_ms = (tv->tv_sec * 1000) + (tv->tv_usec / 1000); /* round up to next millisecond */ if (tv->tv_usec % 1000) timeout_ms++; usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms); r = poll(fds, nfds, timeout_ms); usbi_dbg("poll() returned %d", r); if (r == 0) { free(fds); return handle_timeouts(ctx); } else if (r == -1 && errno == EINTR) { free(fds); return LIBUSB_ERROR_INTERRUPTED; } else if (r < 0) { free(fds); usbi_err(ctx, "poll failed %d err=%d\n", r, errno); return LIBUSB_ERROR_IO; } r = usbi_backend->handle_events(ctx, fds, nfds, r); if (r) usbi_err(ctx, "backend handle_events failed with error %d", r); free(fds); return r;}/* returns the smallest of: * 1. timeout of next URB * 2. user-supplied timeout * returns 1 if there is an already-expired timeout, otherwise returns 0 * and populates out */static int get_next_timeout(libusb_context *ctx, struct timeval *tv, struct timeval *out){ struct timeval timeout; int r = libusb_get_next_timeout(ctx, &timeout); if (r) { /* timeout already expired? */ if (!timerisset(&timeout)) return 1; /* choose the smallest of next URB timeout or user specified timeout */ if (timercmp(&timeout, tv, <)) *out = timeout; else *out = *tv; } else { *out = *tv; } return 0;}/** \ingroup poll * Handle any pending events. * * libusb determines "pending events" by checking if any timeouts have expired * and by checking the set of file descriptors for activity. * * If a zero timeval is passed, this function will handle any already-pending * events and then immediately return in non-blocking style. * * If a non-zero timeval is passed and no events are currently pending, this * function will block waiting for events to handle up until the specified * 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(libusb_context *ctx, struct timeval *tv){ int r; struct timeval poll_timeout; USBI_GET_CONTEXT(ctx); r = get_next_timeout(ctx, tv, &poll_timeout); if (r) { /* timeout already expired */ return handle_timeouts(ctx); }retry: if (libusb_try_lock_events(ctx) == 0) { /* we obtained the event lock: do our own event handling */ 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(ctx); 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(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(ctx, &poll_timeout); libusb_unlock_event_waiters(ctx); if (r < 0) return r; else if (r == 1) return handle_timeouts(ctx); else return 0;}/** \ingroup poll * Handle any pending events in blocking mode with a sensible timeout. This * timeout is currently hardcoded at 2 seconds but we may change this if we * decide other values are more sensible. For finer control over whether this * 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(libusb_context *ctx){ struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; return libusb_handle_events_timeout(ctx, &tv);}/** \ingroup poll * Handle any pending events by polling file descriptors, without checking if * any other threads are already doing so. Must be called with the event lock * held, see libusb_lock_events(). * * This function is designed to be called under the situation where you have * taken the event lock and are calling poll()/select() directly on libusb's * file descriptors (as opposed to using libusb_handle_events() or similar). * 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(libusb_context *ctx, struct timeval *tv){ int r; struct timeval poll_timeout; USBI_GET_CONTEXT(ctx); r = get_next_timeout(ctx, tv, &poll_timeout); if (r) { /* timeout already expired */ return handle_timeouts(ctx); } return handle_events(ctx, &poll_timeout);}/** \ingroup poll * Determine the next internal timeout that libusb needs to handle. You only * need to use this function if you are calling poll() or select() or similar * on libusb's file descriptors yourself - you do not need to use it if you * are calling libusb_handle_events() or a variant directly. * * You should call this function in your main loop in order to determine how * long to wait for select() or poll() to return results. libusb needs to be * called into at this timeout, so you should use it as an upper bound on * your select() or poll() call. * * When the timeout has expired, call into libusb_handle_events_timeout() * (perhaps in non-blocking mode) so that libusb can handle the timeout. * * This function may return 1 (success) and an all-zero timeval. If this is * the case, it indicates that libusb has a timeout that has already expired * 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(libusb_context *ctx, struct timeval *tv){ struct usbi_transfer *transfer; struct timespec cur_ts; struct timeval cur_tv; struct timeval *next_timeout; int r; int found = 0; 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, &ctx->flying_transfers, list) { if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) { found = 1; break; } } pthread_mutex_unlock(&ctx->flying_transfers_lock); if (!found) { usbi_dbg("all URBs have already been processed for timeouts"); return 0; } next_timeout = &transfer->timeout; /* no timeout for next transfer */ if (!timerisset(next_timeout)) { usbi_dbg("no URBs with timeouts, no timeout!"); return 0; } r = clock_gettime(CLOCK_MONOTONIC, &cur_ts); if (r < 0) { usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno); return LIBUSB_ERROR_OTHER; } TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); if (timercmp(&cur_tv, next_timeout, >=)) { usbi_dbg("first timeout already expired"); timerclear(tv); } else { timersub(next_timeout, &cur_tv, tv); usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec); } return 1;}/** \ingroup poll * Register notification functions for file descriptor additions/removals. * These functions will be invoked for every new or removed file descriptor * that libusb uses as an event source. * * 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 * \param user_data User data to be passed back to callbacks (useful for * passing context information) */API_EXPORTED void libusb_set_pollfd_notifiers(libusb_context *ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data){ USBI_GET_CONTEXT(ctx); ctx->fd_added_cb
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -