diff --git a/include/coap3/coap_net.h b/include/coap3/coap_net.h index 3349d860f7..61e62c86fa 100644 --- a/include/coap3/coap_net.h +++ b/include/coap3/coap_net.h @@ -845,6 +845,28 @@ COAP_API void coap_io_do_epoll(coap_context_t *ctx, struct epoll_event *events, */ COAP_API coap_fd_t coap_socket_get_fd(coap_socket_t *socket); +/* + * Get the current libcoap usage of file descriptors that are in a read or write pending state. + * + * @param context The current CoAP context. + * @param read_fds Array to populate with file descriptors in the read pending state. + * @param have_read_fds Updated wth the number of fds found in read pending state. + * @param max_read_fds Maximum size of read_fds[] array. + * @param write_fds Array to populate with file descriptors in the write pending state. + * @param have_write_fds Updated wth the number of fds found in write pending state. + * @param max_write_fds Maximum size of write_fds[] array. + * @param rem_timeout_ms Remaining timeout time to next libcoap activity in milli-secs. + * + * @return @c 1 if successful, else @c 0 if error. + */ +COAP_API unsigned int coap_io_get_fds(coap_context_t *context, coap_fd_t read_fds[], + unsigned int *have_read_fds, + unsigned int max_read_fds, + coap_fd_t write_fds[], + unsigned int *have_write_fds, + unsigned int max_write_fds, + unsigned int *rem_timeout_ms); + /** * Get the libcoap internal flags for a socket. This can be used to * integrate libcoap in an external event loop instead of using one of its diff --git a/include/coap3/coap_net_internal.h b/include/coap3/coap_net_internal.h index e4573579a0..d535fe8345 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -173,10 +173,10 @@ struct coap_context_t { #endif /* COAP_SERVER_SUPPORT */ void *app; /**< application-specific data */ uint32_t max_token_size; /**< Largest token size supported RFC8974 */ + coap_tick_t next_timeout; /**< When the next timeout is to occur */ #ifdef COAP_EPOLL_SUPPORT int epfd; /**< External FD for epoll */ int eptimerfd; /**< Internal FD for timeout */ - coap_tick_t next_timeout; /**< When the next timeout is to occur */ #else /* ! COAP_EPOLL_SUPPORT */ #if !defined(RIOT_VERSION) && !defined(WITH_CONTIKI) fd_set readfds, writefds, exceptfds; /**< Used for select call @@ -779,6 +779,30 @@ int coap_io_process_with_fds_lkd(coap_context_t *ctx, uint32_t timeout_ms, fd_set *exceptfds); #endif /* ! RIOT_VERSION && ! WITH_CONTIKI */ +/* + * Get the current libcoap usage of file descriptors that are in a read or write pending state. + * + * Note: This function must be called in the locked state. + * + * @param context The current CoAP context. + * @param read_fds Array to populate with file descriptors in the read pending state. + * @param have_read_fds Updated wth the number of fds found in read pending state. + * @param max_read_fds Maximum size of read_fds[] array. + * @param write_fds Array to populate with file descriptors in the write pending state. + * @param have_write_fds Updated wth the number of fds found in write pending state. + * @param max_write_fds Maximum size of write_fds[] array. + * @param rem_timeout_ms Remaining timeout time to next libcoap activity in milli-secs. + * + * @return @c 1 if successful, else @c 0 if error. + */ +unsigned int coap_io_get_fds_lkd(coap_context_t *context, coap_fd_t read_fds[], + unsigned int *have_read_fds, + unsigned int max_read_fds, + coap_fd_t write_fds[], + unsigned int *have_write_fds, + unsigned int max_write_fds, + unsigned int *rem_timeout_ms); + /** * Sends a CoAP message to given peer. The memory that is * allocated for the pdu will be released by coap_send_lkd(). diff --git a/libcoap-3.map b/libcoap-3.map index 9c7493a6aa..85c35e53ed 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -123,6 +123,7 @@ global: coap_insert_optlist; coap_io_do_epoll; coap_io_do_io; + coap_io_get_fds; coap_io_pending; coap_io_prepare_epoll; coap_io_prepare_io; diff --git a/libcoap-3.sym b/libcoap-3.sym index 611933f630..79680b9d27 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -121,6 +121,7 @@ coap_host_is_unix_domain coap_insert_optlist coap_io_do_epoll coap_io_do_io +coap_io_get_fds coap_io_pending coap_io_prepare_epoll coap_io_prepare_io diff --git a/man/Makefile.am b/man/Makefile.am index b62f25c39d..983c76cc46 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -135,6 +135,7 @@ install-man: install-man3 install-man5 install-man7 @echo ".so man3/coap_deprecated.3" > coap_set_event_handler.3 @echo ".so man3/coap_deprecated.3" > coap_write.3 @echo ".so man3/coap_io.3" > coap_io_pending.3 + @echo ".so man3/coap_io.3" > coap_io_get_fds.3 @echo ".so man3/coap_io.3" > coap_can_exit.3 @echo ".so man3/coap_io.3" > coap_socket_get_fd.3 @echo ".so man3/coap_io.3" > coap_socket_get_flags.3 diff --git a/man/coap_io.txt.in b/man/coap_io.txt.in index 48155e3762..1623289d63 100644 --- a/man/coap_io.txt.in +++ b/man/coap_io.txt.in @@ -19,6 +19,7 @@ coap_io_do_io, coap_io_prepare_epoll, coap_io_do_epoll, coap_io_pending, +coap_io_get_fds, coap_can_exit, coap_socket_get_fd, coap_socket_get_flags, @@ -51,6 +52,11 @@ size_t _nevents_)*; *int coap_io_pending(coap_context_t *_context_)*; +*unsigned int coap_io_get_fds(coap_context_t *_context_, coap_fd_t _read_fds_[], +unsigned int *_have_read_fds_, unsigned int _max_read_fds_, coap_fd_t _write_fds_[], +unsigned int *_have_write_fds_, unsigned int _max_write_dfs_, +unsigned int *rem_timeout_ms)*; + *int coap_can_exit(coap_context_t *_context_)*; *coap_fd_t coap_socket_get_fd(coap_socket_t *socket);* @@ -69,7 +75,7 @@ or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with DESCRIPTION ----------- After setting up all the contexts, resources, endpoints sessions etc., the -underlying CoAP and (D)TLS need to send (and possible re-send) created packets +underlying CoAP and (D)TLS need to send (and possibly re-send) created packets as well as receive packets for processing. The *coap_io_process*() function is the primary function applications should @@ -204,6 +210,24 @@ The *coap_io_pending*() function checks to see if there are any outstanding i/o requests / responses associated with _context_ as well as if Observe has been set up (client only) and large transfers are in process. +*Function: coap_io_get_fds()* + +The *coap_io_get_fds*() function is used to get all of the libcoap internally +used file descriptors associated with _context_ in a read or write pending state. + +_read_fds_[] is a defined array to hold all the file descriptors that are in a +read pending state with a size of _max_read_fds_. _have_read_fds_ is returned +with the number of file descriptors in _read_fds_[]. + +_write_fds_[] is a defined array to hold all the file descriptors that are in a +write pending state with a size of _max_write_fds_. _have_write_fds_ is returned +with the number of file descriptors in _write_fds_[]. + +_rem_timeout_ms_ is updated with the remaining milli-seconds before coap_io_process() +needs to be called again to handle any internal timeouts. If _rem_timeout_ms_ is 0, +then there is no timeout and the next event will be a change in state of one of +the file descriptors. + *Function: coap_can_exit()* The *coap_can_exit*() function checks to see if there are any outstanding @@ -241,6 +265,8 @@ milli-seconds that need to be waited before the function should next be called. *coap_io_pending*() returns 1 if there is outstanding i/o else returns 0. +*coap_io_get_fds*() returns 1 if file descriptors returned, else returns 0. + *coap_can_exit*() returns 1 if there is nothing outstanding to transmit else returns 0. diff --git a/src/coap_io.c b/src/coap_io.c index 1b16945d07..6781c95ac0 100644 --- a/src/coap_io.c +++ b/src/coap_io.c @@ -534,10 +534,14 @@ coap_update_io_timer(coap_context_t *context, coap_tick_t delay) { #endif /* COAP_DEBUG_WAKEUP_TIMES */ } } -#else /* COAP_EPOLL_SUPPORT */ - (void)context; - (void)delay; -#endif /* COAP_EPOLL_SUPPORT */ +#else /* ! COAP_EPOLL_SUPPORT */ + coap_tick_t now; + + coap_ticks(&now); + if (context->next_timeout == 0 || context->next_timeout > now + delay) { + context->next_timeout = now + delay; + } +#endif /* ! COAP_EPOLL_SUPPORT */ } #endif /* ! WITH_CONTIKI */ @@ -1576,6 +1580,147 @@ coap_io_prepare_io_lkd(coap_context_t *ctx, return (unsigned int)((timeout * 1000 + COAP_TICKS_PER_SECOND - 1) / COAP_TICKS_PER_SECOND); } +/* + * return 0 Insufficient space to hold fds, or fds not supported + * 1 All fds found + */ +COAP_API unsigned int +coap_io_get_fds(coap_context_t *ctx, + coap_fd_t read_fds[], + unsigned int *have_read_fds, + unsigned int max_read_fds, + coap_fd_t write_fds[], + unsigned int *have_write_fds, + unsigned int max_write_fds, + unsigned int *rem_timeout_ms) { + unsigned int ret; + + coap_lock_lock(ctx, return 0); + ret = coap_io_get_fds_lkd(ctx, read_fds, have_read_fds, max_read_fds, write_fds, + have_write_fds, max_write_fds, rem_timeout_ms); + coap_lock_unlock(ctx); + return ret; +} + +#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI) +static int +coap_add_fd(coap_fd_t fd, coap_fd_t this_fds[], unsigned int *have_this_fds, + unsigned int max_this_fds) { + if (*have_this_fds < max_this_fds) { + this_fds[(*have_this_fds)++] = fd; + return 1; + } + coap_log_warn("coap_io_get_fds: Insufficient space for new fd (%u >= %u)\n", *have_this_fds, + max_this_fds); + return 0; +} + +/* + * return 0 Insufficient space to hold fds, or fds not supported + * 1 All fds found + */ +unsigned int +coap_io_get_fds_lkd(coap_context_t *ctx, + coap_fd_t read_fds[], + unsigned int *have_read_fds, + unsigned int max_read_fds, + coap_fd_t write_fds[], + unsigned int *have_write_fds, + unsigned int max_write_fds, + unsigned int *rem_timeout_ms) { + *have_read_fds = 0; + *have_write_fds = 0; + +#ifdef COAP_EPOLL_SUPPORT + (void)write_fds; + (void)max_write_fds;; + + if (!coap_add_fd(ctx->epfd, read_fds, have_read_fds, max_read_fds)) + return 0; + /* epoll is making use of timerfd, so no need to return any timeout */ + *rem_timeout_ms = 0; + return 1; +#else /* ! COAP_EPOLL_SUPPORT */ + coap_session_t *s, *rtmp; + coap_tick_t now; + unsigned int timeout_ms; +#if COAP_SERVER_SUPPORT + coap_endpoint_t *ep; + + LL_FOREACH(ctx->endpoint, ep) { + if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_ACCEPT)) { + if (!coap_add_fd(ep->sock.fd, read_fds, have_read_fds, max_read_fds)) + return 0; + } + if (ep->sock.flags & (COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) { + if (!coap_add_fd(ep->sock.fd, write_fds, have_write_fds, max_write_fds)) + return 0; + } + SESSIONS_ITER_SAFE(ep->sessions, s, rtmp) { + if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_ACCEPT)) { + if (!coap_add_fd(s->sock.fd, read_fds, have_read_fds, max_read_fds)) + return 0; + } + if (s->sock.flags & (COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) { + if (!coap_add_fd(s->sock.fd, write_fds, have_write_fds, max_write_fds)) + return 0; + } + } + } +#endif /* COAP_SERVER_SUPPORT */ + +#if COAP_CLIENT_SUPPORT + SESSIONS_ITER_SAFE(ctx->sessions, s, rtmp) { + if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_ACCEPT)) { + if (!coap_add_fd(s->sock.fd, read_fds, have_read_fds, max_read_fds)) + return 0; + } + if (s->sock.flags & (COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) { + if (!coap_add_fd(s->sock.fd, write_fds, have_write_fds, max_write_fds)) + return 0; + } + } +#endif /* COAP_CLIENT_SUPPORT */ + + coap_ticks(&now); + timeout_ms = (unsigned int)(ctx->next_timeout ? ctx->next_timeout > now ? + ctx->next_timeout - now : 0 : 0) * + 1000 / COAP_TICKS_PER_SECOND; + *rem_timeout_ms = timeout_ms; + return 1; +#endif /* ! COAP_EPOLL_SUPPORT */ +} + +#else /* WITH_LWIP || WITH_CONTIKI */ + +/* + * return 0 Insufficient space to hold fds, or fds not supported + * 1 All fds found + */ +unsigned int +coap_io_get_fds_lkd(coap_context_t *ctx, + coap_fd_t read_fds[], + unsigned int *have_read_fds, + unsigned int max_read_fds, + coap_fd_t write_fds[], + unsigned int *have_write_fds, + unsigned int max_write_fds, + unsigned int *rem_timeout_ms) { + (void)ctx; + (void)read_fds; + (void)max_read_fds; + (void)write_fds; + (void)max_write_fds; + + *have_read_fds = 0; + *have_write_fds = 0; + *rem_timeout_ms = 0; + + coap_log_warn("coap_io_get_fds: Not supported\n"); + return 0; +} +#endif /* WITH_LWIP || WITH_CONTIKI */ + #if !defined(WITH_LWIP) && !defined(CONTIKI) && !defined(RIOT_VERSION) COAP_API int coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) { @@ -1626,6 +1771,7 @@ coap_io_process_with_fds_lkd(coap_context_t *ctx, uint32_t timeout_ms, timeout = coap_io_prepare_io_lkd(ctx, ctx->sockets, (sizeof(ctx->sockets) / sizeof(ctx->sockets[0])), &ctx->num_sockets, before); + ctx->next_timeout = timeout ? timeout + before : 0; if (ereadfds) { ctx->readfds = *ereadfds; diff --git a/src/coap_io_contiki.c b/src/coap_io_contiki.c index cf553da001..be034d1b11 100644 --- a/src/coap_io_contiki.c +++ b/src/coap_io_contiki.c @@ -39,6 +39,8 @@ on_io_timer_expired(void *ptr) { void coap_update_io_timer(coap_context_t *ctx, coap_tick_t delay) { + coap_tick_t now; + if (!ctimer_expired(&ctx->io_timer)) { ctimer_stop(&ctx->io_timer); } @@ -50,6 +52,10 @@ coap_update_io_timer(coap_context_t *ctx, coap_tick_t delay) { on_io_timer_expired, ctx); } + coap_ticks(&now); + if (ctx->next_timeout == 0 || ctx->next_timeout > now + delay) { + ctx->next_timeout = now + delay; + } } static void