X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fsession.c;h=f253dbd8e9e98760b290841cff789917aa7cf942;hb=4b9e2532135a07cfda6e2ac7a68c69486411b2b7;hp=aaeda1cc09ed199eebd59dd79078b6e0e6ef9bcf;hpb=faa5d7d997718acdd2b293a32dd62e28268b391f;p=libsigrok.git diff --git a/src/session.c b/src/session.c index aaeda1cc..f253dbd8 100644 --- a/src/session.c +++ b/src/session.c @@ -47,6 +47,7 @@ struct source { int64_t timeout; /* microseconds */ int64_t due; /* microseconds */ + sr_receive_data_callback cb; void *cb_data; @@ -55,7 +56,12 @@ struct source { */ gintptr poll_object; - gboolean is_usb; + /* Number of fds to poll for this source. This will be 0 for timer + * sources, 1 for normal I/O sources, and 1 or more for libusb I/O + * sources on Unix platforms. + */ + int num_fds; + gboolean triggered; }; @@ -383,8 +389,6 @@ static gboolean sr_session_check_aborted(struct sr_session *session) return stop; } -static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object); - /** * Poll the session's event sources. * @@ -397,6 +401,7 @@ static int sr_session_iteration(struct sr_session *session) int64_t start_time, stop_time, min_due, due; int timeout_ms; unsigned int i; + int k, fd_index; int ret; int fd; int revents; @@ -404,7 +409,8 @@ static int sr_session_iteration(struct sr_session *session) struct source *source; GPollFD *pollfd; gintptr poll_object; -#ifdef HAVE_LIBUSB_1_0 +#if HAVE_LIBUSB_1_0 + int64_t usb_timeout; int64_t usb_due; struct timeval tv; #endif @@ -421,7 +427,7 @@ static int sr_session_iteration(struct sr_session *session) min_due = source->due; source->triggered = FALSE; } -#ifdef HAVE_LIBUSB_1_0 +#if HAVE_LIBUSB_1_0 usb_due = INT64_MAX; if (session->ctx->usb_source_present) { ret = libusb_get_next_timeout(session->ctx->libusb_ctx, &tv); @@ -430,10 +436,14 @@ static int sr_session_iteration(struct sr_session *session) libusb_error_name(ret)); return SR_ERR; } else if (ret == 1) { - usb_due = start_time + tv.tv_usec - + (int64_t)tv.tv_sec * G_USEC_PER_SEC; + usb_timeout = (int64_t)tv.tv_sec * G_USEC_PER_SEC + + tv.tv_usec; + usb_due = start_time + usb_timeout; if (usb_due < min_due) min_due = usb_due; + + sr_spew("poll: next USB timeout %g ms", + 1e-3 * usb_timeout); } } #endif @@ -444,6 +454,9 @@ static int sr_session_iteration(struct sr_session *session) else timeout_ms = 0; + sr_spew("poll enter: %u sources, %u fds, %d ms timeout", + session->sources->len, session->pollfds->len, timeout_ms); + ret = g_poll((GPollFD *)session->pollfds->data, session->pollfds->len, timeout_ms); #ifdef G_OS_UNIX @@ -458,30 +471,44 @@ static int sr_session_iteration(struct sr_session *session) } #endif stop_time = g_get_monotonic_time(); + + sr_spew("poll leave: %g ms elapsed, %d events", + 1e-3 * (stop_time - start_time), ret); + triggered = FALSE; stopped = FALSE; + fd_index = 0; for (i = 0; i < session->sources->len; ++i) { source = &g_array_index(session->sources, struct source, i); - if (source->triggered) - continue; /* already handled */ poll_object = source->poll_object; fd = (int)poll_object; revents = 0; - if (i < session->pollfds->len) { - pollfd = &g_array_index(session->pollfds, GPollFD, i); + for (k = 0; k < source->num_fds; ++k) { + pollfd = &g_array_index(session->pollfds, + GPollFD, fd_index + k); fd = pollfd->fd; - if (ret > 0) - revents = pollfd->revents; + revents |= pollfd->revents; } + fd_index += source->num_fds; + + if (source->triggered) + continue; /* already handled */ if (ret > 0 && revents == 0) continue; /* skip timeouts if any I/O event occurred */ + /* Make invalid to avoid confusion in case of multiple FDs. */ + if (source->num_fds > 1) + fd = -1; + if (ret <= 0) + revents = 0; + due = source->due; -#ifdef HAVE_LIBUSB_1_0 - if (source->is_usb && usb_due < due) +#if HAVE_LIBUSB_1_0 + if (usb_due < due && poll_object + == (gintptr)session->ctx->libusb_ctx) due = usb_due; #endif if (revents == 0 && stop_time < due) @@ -498,7 +525,7 @@ static int sr_session_iteration(struct sr_session *session) * Invoke the source's callback on an event or timeout. */ if (!source->cb(fd, revents, source->cb_data)) - _sr_session_source_remove(session, poll_object); + sr_session_source_remove_internal(session, poll_object); /* * We want to take as little time as possible to stop * the session if we have been told to do so. Therefore, @@ -509,6 +536,7 @@ static int sr_session_iteration(struct sr_session *session) stopped = sr_session_check_aborted(session); /* Restart loop as the sources list may have changed. */ + fd_index = 0; i = 0; } @@ -872,33 +900,49 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi, * Add an event source for a file descriptor. * * @param session The session to use. Must not be NULL. - * @param pollfd The GPollFD. + * @param[in] pollfds The FDs to poll, or NULL if @a num_fds is 0. + * @param[in] num_fds Number of FDs in the array. * @param[in] timeout Max time in ms to wait before the callback is called, * or -1 to wait indefinitely. * @param cb Callback function to add. Must not be NULL. * @param cb_data Data for the callback function. Can be NULL. * @param poll_object Handle by which the source is identified - * @param is_usb TRUE for a libusb polling source * * @retval SR_OK Success. * @retval SR_ERR_ARG Invalid argument. + * @retval SR_ERR An event source for @a poll_object is already installed. */ SR_PRIV int sr_session_source_add_internal(struct sr_session *session, - GPollFD *pollfd, int timeout, sr_receive_data_callback cb, - void *cb_data, gintptr poll_object, gboolean is_usb) + const GPollFD *pollfds, int num_fds, int timeout, + sr_receive_data_callback cb, void *cb_data, + gintptr poll_object) { struct source src; + unsigned int i; + /* Note: cb_data can be NULL, that's not a bug. */ if (!cb) { sr_err("%s: cb was NULL", __func__); return SR_ERR_ARG; } - /* Note: cb_data can be NULL, that's not a bug. */ - + if (!pollfds && num_fds != 0) { + sr_err("%s: pollfds was NULL", __func__); + return SR_ERR_ARG; + } + /* Make sure that poll_object is unique. + */ + for (i = 0; i < session->sources->len; ++i) { + if (g_array_index(session->sources, struct source, i) + .poll_object == poll_object) { + sr_err("Event source for object %" G_GINTPTR_FORMAT + " already installed.", poll_object); + return SR_ERR; + } + } src.cb = cb; src.cb_data = cb_data; src.poll_object = poll_object; - src.is_usb = is_usb; + src.num_fds = num_fds; src.triggered = FALSE; if (timeout >= 0) { @@ -908,15 +952,10 @@ SR_PRIV int sr_session_source_add_internal(struct sr_session *session, src.timeout = -1; src.due = INT64_MAX; } + g_array_append_val(session->sources, src); - if (pollfd) { - /* I/O source */ - g_array_insert_val(session->sources, session->pollfds->len, src); - g_array_append_vals(session->pollfds, pollfd, 1); - } else { - /* Timer source */ - g_array_append_val(session->sources, src); - } + if (num_fds > 0) + g_array_append_vals(session->pollfds, pollfds, num_fds); return SR_OK; } @@ -951,7 +990,7 @@ SR_API int sr_session_source_add(struct sr_session *session, int fd, p.revents = 0; return sr_session_source_add_internal(session, - (fd < 0) ? NULL : &p, timeout, cb, cb_data, fd, FALSE); + &p, (fd < 0) ? 0 : 1, timeout, cb, cb_data, fd); } /** @@ -977,8 +1016,8 @@ SR_API int sr_session_source_add_pollfd(struct sr_session *session, sr_err("%s: pollfd was NULL", __func__); return SR_ERR_ARG; } - return sr_session_source_add_internal(session, pollfd, timeout, - cb, cb_data, (gintptr)pollfd, FALSE); + return sr_session_source_add_internal(session, pollfd, 1, + timeout, cb, cb_data, (gintptr)pollfd); } /** @@ -1010,35 +1049,51 @@ SR_API int sr_session_source_add_channel(struct sr_session *session, p.events = events; p.revents = 0; #endif - return sr_session_source_add_internal(session, &p, timeout, cb, - cb_data, (gintptr)channel, FALSE); + return sr_session_source_add_internal(session, &p, 1, + timeout, cb, cb_data, (gintptr)channel); } /** - * Remove the source belonging to the specified channel. + * Remove the source identified by the specified poll object. * * @param session The session to use. Must not be NULL. * @param poll_object The channel for which the source should be removed. * * @retval SR_OK Success - * @retval SR_ERR_ARG Invalid arguments - * @retval SR_ERR_BUG Internal error + * @retval SR_ERR_BUG No event source for poll_object found. */ -static int _sr_session_source_remove(struct sr_session *session, gintptr poll_object) +SR_PRIV int sr_session_source_remove_internal(struct sr_session *session, + gintptr poll_object) { + struct source *source; unsigned int i; + int fd_index = 0; for (i = 0; i < session->sources->len; ++i) { - if (g_array_index(session->sources, struct source, i) - .poll_object == poll_object) { + source = &g_array_index(session->sources, struct source, i); + if (source->poll_object == poll_object) { + if (source->num_fds > 0) + g_array_remove_range(session->pollfds, + fd_index, source->num_fds); g_array_remove_index(session->sources, i); - if (i < session->pollfds->len) - g_array_remove_index(session->pollfds, i); - break; + /* + * This is a bit of a hack. To be removed when + * porting over to the GLib main loop. + */ + if (poll_object == (gintptr)session->ctx->libusb_ctx) + session->ctx->usb_source_present = FALSE; + return SR_OK; } + fd_index += source->num_fds; } - return SR_OK; + /* Trying to remove an already removed event source is problematic + * since the poll_object handle may have been reused in the meantime. + */ + sr_warn("Cannot remove non-existing event source for object %" + G_GINTPTR_FORMAT ".", poll_object); + + return SR_ERR_BUG; } /** @@ -1055,7 +1110,7 @@ static int _sr_session_source_remove(struct sr_session *session, gintptr poll_ob */ SR_API int sr_session_source_remove(struct sr_session *session, int fd) { - return _sr_session_source_remove(session, fd); + return sr_session_source_remove_internal(session, fd); } /** @@ -1077,7 +1132,7 @@ SR_API int sr_session_source_remove_pollfd(struct sr_session *session, sr_err("%s: pollfd was NULL", __func__); return SR_ERR_ARG; } - return _sr_session_source_remove(session, (gintptr)pollfd); + return sr_session_source_remove_internal(session, (gintptr)pollfd); } /** @@ -1099,7 +1154,7 @@ SR_API int sr_session_source_remove_channel(struct sr_session *session, sr_err("%s: channel was NULL", __func__); return SR_ERR_ARG; } - return _sr_session_source_remove(session, (gintptr)channel); + return sr_session_source_remove_internal(session, (gintptr)channel); } static void copy_src(struct sr_config *src, struct sr_datafeed_meta *meta_copy)