diff --git a/TODO b/TODO index c0b0dba08b..34b3a15671 100644 --- a/TODO +++ b/TODO @@ -19,7 +19,7 @@ nuttx/: (9) Kernel/Protected Build (3) C++ Support (5) Binary loaders (binfmt/) - (18) Network (net/, drivers/net) + (17) Network (net/, drivers/net) (4) USB (drivers/usbdev, drivers/usbhost) (2) Other drivers (drivers/) (9) Libraries (libs/libc/, libs/libm/) @@ -1663,29 +1663,6 @@ o Network (net/, drivers/net) anything but a well-known point-to-point configuration impossible. - Title: SO_LINGER IMPLEMENTATION IS INCORRECT - Description: Support for the SO_LINGER socket option is implemented but - not correctly. Currently, it simply adds a timeout to the - "normal" delay for the FIN to be sent with an additional - timeout. That is not even close to the required behavior. - Per OpenGroup.org: - - SO_LINGER - Lingers on a close() if data is present. This option - controls the action taken when unsent messages queue - on a socket and close() is performed. If SO_LINGER - is set, the system shall block the calling thread - during close() until it can transmit the data or - until the time expires. If SO_LINGER is not specified, - and close() is issued, the system handles the call - in a way that allows the calling thread to continue - as quickly as possible. This option takes a linger - structure, as defined in the header, - to specify the state of the option and linger interval. - - Status: Open - Priority: Medium Low. - o USB (drivers/usbdev, drivers/usbhost) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/nuttx/wqueue.h b/include/nuttx/wqueue.h index e9f1e885d9..d2f1618f49 100644 --- a/include/nuttx/wqueue.h +++ b/include/nuttx/wqueue.h @@ -284,11 +284,13 @@ struct work_s enum work_evtype_e { - WORK_IOB_AVAIL = 1, /* Notify availability of an IOB */ - WORK_NET_DOWN, /* Notify that the network is down */ - WORK_TCP_READAHEAD, /* Notify that TCP read-ahead data is available */ - WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */ - WORK_UDP_READAHEAD /* Notify that TCP read-ahead data is available */ + WORK_IOB_AVAIL = 1, /* Notify availability of an IOB */ + WORK_NET_DOWN, /* Notify that the network is down */ + WORK_TCP_READAHEAD, /* Notify that TCP read-ahead data is available */ + WORK_TCP_WRITEBUFFER, /* Notify that TCP write buffer is empty */ + WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */ + WORK_UDP_READAHEAD, /* Notify that UDP read-ahead data is available */ + WORK_UDP_WRITEBUFFER /* Notify that UDP write buffer is empty */ }; /* This structure describes one notification and is provided as input to diff --git a/net/inet/Make.defs b/net/inet/Make.defs index 8509acf025..20f2683bf3 100644 --- a/net/inet/Make.defs +++ b/net/inet/Make.defs @@ -52,6 +52,10 @@ ifeq ($(CONFIG_NET_IPv6),y) SOCK_CSRCS += ipv6_setsockopt.c ipv6_getsockname.c ipv6_getpeername.c endif +ifeq ($(CONFIG_NET_SOLINGER),y) +SOCK_CSRCS += inet_txdrain.c +endif + # Include inet build support DEPPATH += --dep-path inet diff --git a/net/inet/inet.h b/net/inet/inet.h index 73b44e875d..2c0f4c7e98 100644 --- a/net/inet/inet.h +++ b/net/inet/inet.h @@ -305,6 +305,29 @@ ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, int inet_close(FAR struct socket *psock); +/**************************************************************************** + * Name: inet_txdrain + * + * Description: + * Wait for any buffered Tx data to be sent from the socket. This is part + * of the implementation of SO_LINGER + * + * Parameters: + * psock - Pointer to the socket structure instance + * abstime - The absolute time when the timeout will occur + * + * Returned Value: + * Zero (OK) is returned on success; a negated error value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_SOLINGER +struct timespec; +int inet_txdrain(FAR struct socket *psock, + FAR const struct timespec *abstime); +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/net/inet/inet_close.c b/net/inet/inet_close.c index 5f8f29f1bd..092312dabe 100644 --- a/net/inet/inet_close.c +++ b/net/inet/inet_close.c @@ -56,10 +56,6 @@ #include #include -#ifdef CONFIG_NET_SOLINGER -# include -#endif - #include "netdev/netdev.h" #include "devif/devif.h" #include "tcp/tcp.h" @@ -81,9 +77,6 @@ struct tcp_close_s FAR struct socket *cl_psock; /* Reference to the TCP socket */ sem_t cl_sem; /* Signals disconnect completion */ int cl_result; /* The result of the close */ -#ifdef CONFIG_NET_SOLINGER - clock_t cl_start; /* Time close started (in ticks) */ -#endif }; #endif @@ -91,51 +84,6 @@ struct tcp_close_s * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: tcp_close_timeout - * - * Description: - * Check for a timeout on a lingering close. - * - * Input Parameters: - * pstate - close state structure - * - * Returned Value: - * TRUE:timeout FALSE:no timeout - * - * Assumptions: - * The network is locked - * - ****************************************************************************/ - -#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER) -static inline int tcp_close_timeout(FAR struct tcp_close_s *pstate) -{ - FAR struct socket *psock = 0; - - /* Make sure that we are performing a lingering close */ - - if (pstate != NULL) - { - /* Yes Check for a timeout configured via setsockopts(SO_LINGER). - * If none... we well let the send wait forever. - */ - - psock = pstate->cl_psock; - if (psock && psock->s_linger != 0) - { - /* Check if the configured timeout has elapsed */ - - return net_timeo(pstate->cl_start, psock->s_linger); - } - } - - /* No timeout */ - - return FALSE; -} -#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */ - /**************************************************************************** * Name: tcp_close_eventhandler * @@ -202,19 +150,6 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev, } } -#ifdef CONFIG_NET_SOLINGER - /* Check for a timeout. */ - - else if (pstate && tcp_close_timeout(pstate)) - { - /* Yes.. Wake up the waiting thread and report the timeout */ - - nerr("ERROR: CLOSE timeout\n"); - pstate->cl_result = -ETIMEDOUT; - goto end_wait; - } -#endif /* CONFIG_NET_SOLINGER */ - #ifdef CONFIG_NET_TCP_WRITE_BUFFERS /* Check if all outstanding bytes have been ACKed */ @@ -337,6 +272,46 @@ static inline int tcp_close_disconnect(FAR struct socket *psock) conn = (FAR struct tcp_conn_s *)psock->s_conn; DEBUGASSERT(conn != NULL); +#ifdef CONFIG_NET_SOLINGER + /* SO_LINGER + * Lingers on a close() if data is present. This option controls the + * action taken when unsent messages queue on a socket and close() is + * performed. If SO_LINGER is set, the system shall block the calling + * thread during close() until it can transmit the data or until the + * time expires. If SO_LINGER is not specified, and close() is issued, + * the system handles the call in a way that allows the calling thread + * to continue as quickly as possible. This option takes a linger + * structure, as defined in the header, to specify the + * state of the option and linger interval. + */ + + linger = _SO_GETOPT(psock->s_options, SO_LINGER); + if (linger) + { + /* Get the current time */ + + DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime)); + + /* NOTE: s_linger's unit is deciseconds so we don't need to update + * abstime.tv_nsec here. + */ + + abstime.tv_sec += psock->s_linger / DSEC_PER_SEC; + + /* Wait until abstime for the buffered TX data to be sent. */ + + ret = inet_txdrain(psock, &abstime); + if (ret < 0) + { + /* inet_txdrain may fail, but that won't stop us from closing the + * socket. + */ + + nerr("ERROR: inet_txdrain() failed: %d\n", ret); + } + } +#endif + #ifdef CONFIG_NET_TCP_WRITE_BUFFERS /* If we have a semi-permanent write buffer callback in place, then * is needs to be be nullified. @@ -387,63 +362,13 @@ static inline int tcp_close_disconnect(FAR struct socket *psock) nxsem_init(&state.cl_sem, 0, 0); nxsem_setprotocol(&state.cl_sem, SEM_PRIO_NONE); -#ifdef CONFIG_NET_SOLINGER - /* Record the time that we started the wait (in ticks) */ - - state.cl_start = clock_systimer(); -#endif - /* Notify the device driver of the availability of TX data */ tcp_close_txnotify(psock, conn); /* Wait for the disconnect event */ -#ifdef CONFIG_NET_SOLINGER - /* A non-NULL value of the priv field means that lingering is - * enabled. - * - * REVISIT: SO_LINGER is not really implemented. Per OpenGroup.org: - * - * SO_LINGER - * Lingers on a close() if data is present. This option - * controls the action taken when unsent messages queue - * on a socket and close() is performed. If SO_LINGER - * is set, the system shall block the calling thread - * during close() until it can transmit the data or - * until the time expires. If SO_LINGER is not specified, - * and close() is issued, the system handles the call - * in a way that allows the calling thread to continue - * as quickly as possible. This option takes a linger - * structure, as defined in the header, - * to specify the state of the option and linger interval. - * - * Here it merely adds a pointless timeout on top of the normal - * close operation. It should first wait for all data in the - * protocol-specific write buffers to drain. - */ - - linger = _SO_GETOPT(psock->s_options, SO_LINGER); - if (linger) - { - DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime)); - - /* NOTE: s_linger's unit is deciseconds, - * so we don't need to update abstime.tv_nsec here. - */ - - abstime.tv_sec += psock->s_linger / DSEC_PER_SEC; - - if (-ETIMEDOUT == net_timedwait(&state.cl_sem, &abstime)) - { - state.cl_result = -ETIMEDOUT; - } - } - else -#endif - { - (void)net_lockedwait(&state.cl_sem); - } + (void)net_lockedwait(&state.cl_sem); /* We are now disconnected */ diff --git a/net/inet/inet_txdrain.c b/net/inet/inet_txdrain.c new file mode 100644 index 0000000000..bdec90b430 --- /dev/null +++ b/net/inet/inet_txdrain.c @@ -0,0 +1,110 @@ +/**************************************************************************** + * net/inet/inet_txdrain.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "udp/udp.h" +#include "tcp/tcp.h" +#include "inet/inet.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_txdrain + * + * Description: + * Wait for any buffered Tx data to be sent from the socket. This is part + * of the implementation of SO_LINGER + * + * Parameters: + * psock - Pointer to the socket structure instance + * abstime - The absolute time when the timeout will occur + * + * Returned Value: + * Zero (OK) is returned on success; a negated error value is returned on + * any failure. + * + ****************************************************************************/ + +int inet_txdrain(FAR struct socket *psock, + FAR const struct timespec *abstime) +{ + int ret = OK; + + DEBUGASSERT(psock != NULL); + + /* Draining depends on the socket family */ + + switch (psock->s_type) + { +#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_TCP_WRITE_BUFFERS) + case SOCK_STREAM: + { + ret = tcp_txdrain(psock, abstime); + } + break; +#endif + +#if defined(NET_UDP_HAVE_STACK) && defined(CONFIG_NET_UDP_WRITE_BUFFERS) + case SOCK_DGRAM: + { + ret = udp_txdrain(psock, abstime); + } + break; +#endif + + /* Other protocols do no support write buffering */ + + default: + break; + } + + return ret; +} diff --git a/net/socket/Kconfig b/net/socket/Kconfig index 64a27cbb39..a22948bd6a 100644 --- a/net/socket/Kconfig +++ b/net/socket/Kconfig @@ -42,8 +42,12 @@ if NET_SOCKOPTS config NET_SOLINGER bool "SO_LINGER socket option" default n + depends on NET_TCP_WRITE_BUFFERS || NET_UDP_WRITE_BUFFERS + select TCP_NOTIFIER if NET_TCP + select UDP_NOTIFIER if NET_UDP ---help--- - Enable or disable support for the SO_LINGER socket option. + Enable or disable support for the SO_LINGER socket option. Requires + write buffer support. endif # NET_SOCKOPTS endmenu # Socket Support diff --git a/net/tcp/Make.defs b/net/tcp/Make.defs index d06b0094cd..6d67b02c58 100644 --- a/net/tcp/Make.defs +++ b/net/tcp/Make.defs @@ -56,6 +56,9 @@ ifeq ($(CONFIG_NET_TCP_READAHEAD),y) SOCK_CSRCS += tcp_netpoll.c ifeq ($(CONFIG_TCP_NOTIFIER),y) SOCK_CSRCS += tcp_notifier.c +ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y) +SOCK_CSRCS += tcp_txdrain.c +endif endif endif diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 49c12294c2..3088559436 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -1558,7 +1558,7 @@ int tcp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds); * Input Parameters: * worker - The worker function to execute on the high priority work * queue when data is available in the TCP read-ahead buffer. - * conn - The TCP connection where read-ahead data is needed. + * conn - The TCP connection where read-ahead data is needed. * arg - A user-defined argument that will be available to the worker * function when it runs. * @@ -1581,7 +1581,40 @@ int tcp_readahead_notifier_setup(worker_t worker, #endif /**************************************************************************** - * Name: tcp_readahead_disconnect_setup + * Name: tcp_writebuffer_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function when an TCP write + * buffer is emptied. The worker function will execute on the high + * priority worker thread. + * + * Input Parameters: + * worker - The worker function to execute on the high priority work + * queue when all buffer TX data has been sent. + * conn - The TCP connection where buffer write data is pending. + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * > 0 - The signal notification is in place. The returned value is a + * key that may be used later in a call to + * tcp_notifier_teardown(). + * == 0 - There is already buffered read-ahead data. No signal + * notification will be provided. + * < 0 - An unexpected error occurred and no signal will be sent. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_TCP_NOTIFIER +int tcp_writebuffer_notifier_setup(worker_t worker, + FAR struct tcp_conn_s *conn, + FAR void *arg); +#endif + +/**************************************************************************** + * Name: tcp_disconnect_notifier_setup * * Description: * Set up to perform a callback to the worker function if the TCP @@ -1607,9 +1640,9 @@ int tcp_readahead_notifier_setup(worker_t worker, ****************************************************************************/ #ifdef CONFIG_TCP_NOTIFIER -int tcp_readahead_disconnect_setup(worker_t worker, - FAR struct tcp_conn_s *conn, - FAR void *arg); +int tcp_disconnect_notifier_setup(worker_t worker, + FAR struct tcp_conn_s *conn, + FAR void *arg); #endif /**************************************************************************** @@ -1656,10 +1689,35 @@ int tcp_notifier_teardown(int key); * ****************************************************************************/ -#ifdef CONFIG_TCP_NOTIFIER +#if defined(CONFIG_NET_TCP_READAHEAD) && defined(CONFIG_TCP_NOTIFIER) void tcp_readahead_signal(FAR struct tcp_conn_s *conn); #endif +/**************************************************************************** + * Name: tcp_writebuffer_signal + * + * Description: + * All buffer Tx data has been sent. Signal all threads waiting for the + * write buffers to become empty. + * + * When write buffer becomes empty, *all* of the workers waiting + * for that event data will be executed. If there are multiple workers + * waiting for read-ahead data then only the first to execute will get the + * data. Others will need to call tcp_writebuffer_notifier_setup() once + * again. + * + * Input Parameters: + * conn - The TCP connection where read-ahead data was just buffered. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_TCP_NOTIFIER) +void tcp_writebuffer_signal(FAR struct tcp_conn_s *conn); +#endif + /**************************************************************************** * Name: tcp_disconnect_signal * @@ -1679,6 +1737,30 @@ void tcp_readahead_signal(FAR struct tcp_conn_s *conn); void tcp_disconnect_signal(FAR struct tcp_conn_s *conn); #endif +/**************************************************************************** + * Name: tcp_txdrain + * + * Description: + * Wait for all write buffers to be sent (or for a timeout to occur). + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * abstime - The absolute time when the timeout will occur + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_TCP_NOTIFIER) +struct timespec; +int tcp_txdrain(FAR struct socket *psock, + FAR const struct timespec *abstime); +#else +# define udp_txdrain(conn, abstime) (0) +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/net/tcp/tcp_notifier.c b/net/tcp/tcp_notifier.c index ccf63a4171..c9a9861a2e 100644 --- a/net/tcp/tcp_notifier.c +++ b/net/tcp/tcp_notifier.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/tcp/tcp_notifier.c * - * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Copyright (C) 2018-2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -84,6 +84,7 @@ int tcp_readahead_notifier_setup(worker_t worker, FAR struct tcp_conn_s *conn, FAR void *arg) { +#ifdef CONFIG_NET_TCP_READAHEAD struct work_notifier_s info; DEBUGASSERT(worker != NULL); @@ -106,10 +107,72 @@ int tcp_readahead_notifier_setup(worker_t worker, info.worker = worker; return work_notifier_setup(&info); +#else + return 0; +#endif } /**************************************************************************** - * Name: tcp_readahead_disconnect_setup + * Name: tcp_writebuffer_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function when an TCP write + * buffer is emptied. The worker function will execute on the high + * priority worker thread. + * + * Input Parameters: + * worker - The worker function to execute on the high priority work + * queue when all buffer TX data has been sent. + * conn - The TCP connection where buffer write data is pending. + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * > 0 - The signal notification is in place. The returned value is a + * key that may be used later in a call to + * tcp_notifier_teardown(). + * == 0 - There is already buffered read-ahead data. No signal + * notification will be provided. + * < 0 - An unexpected error occurred and no signal will be sent. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +int tcp_writebuffer_notifier_setup(worker_t worker, + FAR struct tcp_conn_s *conn, + FAR void *arg) +{ +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + struct work_notifier_s info; + + DEBUGASSERT(worker != NULL); + + /* If the write buffers are already empty, then return zero without + * setting up the notification. + */ + + if (sq_empty(&conn->write_q) && sq_empty(&conn->unacked_q)) + { + return 0; + } + + /* Otherwise, this is just a simple wrapper around work_notifer_setup(). */ + + info.evtype = WORK_TCP_WRITEBUFFER; + info.qid = LPWORK; + info.qualifier = conn; + info.arg = arg; + info.worker = worker; + + return work_notifier_setup(&info); +#else + return 0; +#endif +} + +/**************************************************************************** + * Name: tcp_disconnect_notifier_setup * * Description: * Set up to perform a callback to the worker function if the TCP @@ -133,9 +196,9 @@ int tcp_readahead_notifier_setup(worker_t worker, * ****************************************************************************/ -int tcp_readahead_disconnect_setup(worker_t worker, - FAR struct tcp_conn_s *conn, - FAR void *arg) +int tcp_disconnect_notifier_setup(worker_t worker, + FAR struct tcp_conn_s *conn, + FAR void *arg) { struct work_notifier_s info; @@ -206,12 +269,44 @@ int tcp_notifier_teardown(int key) * ****************************************************************************/ +#ifdef CONFIG_NET_TCP_READAHEAD void tcp_readahead_signal(FAR struct tcp_conn_s *conn) { /* This is just a simple wrapper around work_notifier_signal(). */ return work_notifier_signal(WORK_TCP_READAHEAD, conn); } +#endif + +/**************************************************************************** + * Name: tcp_writebuffer_signal + * + * Description: + * All buffer Tx data has been sent. Signal all threads waiting for the + * write buffers to become empty. + * + * When write buffer becomes empty, *all* of the workers waiting + * for that event data will be executed. If there are multiple workers + * waiting for read-ahead data then only the first to execute will get the + * data. Others will need to call tcp_writebuffer_notifier_setup() once + * again. + * + * Input Parameters: + * conn - The TCP connection where read-ahead data was just buffered. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS +void tcp_writebuffer_signal(FAR struct tcp_conn_s *conn) +{ + /* This is just a simple wrapper around work_notifier_signal(). */ + + return work_notifier_signal(WORK_TCP_WRITEBUFFER, conn); +} +#endif /**************************************************************************** * Name: tcp_disconnect_signal diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 5f8f8072cf..431322404a 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -158,6 +158,37 @@ static void psock_insert_segment(FAR struct tcp_wrbuffer_s *wrb, } } +/**************************************************************************** + * Name: psock_writebuffer_notify + * + * Description: + * The TCP connection has been lost. Free all write buffers. + * + * Input Parameters: + * psock The socket structure + * conn The connection structure associated with the socket + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_TCP_NOTIFIER +static void psock_writebuffer_notify(FAR struct tcp_conn_s *conn) +{ + /* Check if all write buffers have been sent and ACKed */ + + if (sq_empty(&conn->write_q) && sq_empty(&conn->unacked_q)) + { + /* Notify any waiters that the write buffers have been drained. */ + + tcp_writebuffer_signal(conn); + } +} +#else +# define psock_writebuffer_notify(conn) +#endif + /**************************************************************************** * Name: psock_lost_connection * @@ -207,6 +238,11 @@ static inline void psock_lost_connection(FAR struct socket *psock, sq_init(&conn->unacked_q); sq_init(&conn->write_q); + + /* Notify any waiters if the write buffers have been drained. */ + + psock_writebuffer_notify(conn); + conn->sent = 0; conn->sndseq_max = 0; } @@ -508,6 +544,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, /* And return the write buffer to the pool of free buffers */ tcp_wrbuffer_release(wrb); + + /* Notify any waiters if the write buffers have been + * drained. + */ + + psock_writebuffer_notify(conn); } else { @@ -664,6 +706,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, tcp_wrbuffer_release(wrb); + /* Notify any waiters if the write buffers have been + * drained. + */ + + psock_writebuffer_notify(conn); + /* NOTE expired is different from un-ACKed, it is designed to * represent the number of segments that have been sent, * retransmitted, and un-ACKed, if expired is not zero, the @@ -722,6 +770,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, tcp_wrbuffer_release(wrb); + /* Notify any waiters if the write buffers have been + * drained. + */ + + psock_writebuffer_notify(conn); + /* NOTE expired is different from un-ACKed, it is designed to * represent the number of segments that have been sent, * retransmitted, and un-ACKed, if expired is not zero, the diff --git a/net/tcp/tcp_txdrain.c b/net/tcp/tcp_txdrain.c new file mode 100644 index 0000000000..91c63ce617 --- /dev/null +++ b/net/tcp/tcp_txdrain.c @@ -0,0 +1,146 @@ +/**************************************************************************** + * net/tcp/tcp_txdrain.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tcp/tcp.h" + +#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_TCP_NOTIFIER) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: txdrain_worker + * + * Description: + * Called with the write buffers have all been sent. + * + * Input Parameters: + * arg - The semaphore that will wake up tcp_txdrain + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void txdrain_worker(FAR void *arg) +{ + FAR sem_t *waitsem = (FAR sem_t *)arg; + DEBUGASSERT(waitsem != NULL); + sem_post(waitsem); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_txdrain + * + * Description: + * Wait for all write buffers to be sent (or for a timeout to occur). + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * abstime - The absolute time when the timeout will occur + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +int tcp_txdrain(FAR struct socket *psock, + FAR const struct timespec *abstime) +{ + FAR struct tcp_conn_s *conn; + sem_t waitsem; + int ret; + + DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && psock->s_conn != NULL); + DEBUGASSERT(psock->s_type == SOCK_DGRAM); + + conn = (FAR struct tcp_conn_s *)psock->s_conn; + + /* Initialize the wait semaphore */ + + nxsem_init(&waitsem, 0, 0); + + /* The following needs to be done with the network stable */ + + net_lock(); + ret = tcp_writebuffer_notifier_setup(txdrain_worker, conn, &waitsem); + if (ret > 0) + { + int key = ret; + + /* There is pending write data.. wait for it to drain. */ + + do + { + ret = net_timedwait(&waitsem, abstime); + } + while (ret == EINTR); + + /* Tear down the notifier (in case we timed out or were canceled) */ + + if (ret < 0) + { + tcp_notifier_teardown(key); + } + } + + net_unlock(); + nxsem_destroy(&waitsem); + return ret; +} + +#endif /* CONFIG_NET_TCP_WRITE_BUFFERS && CONFIG_TCP_NOTIFIER */ diff --git a/net/udp/Kconfig b/net/udp/Kconfig index d6bf806988..e447002782 100644 --- a/net/udp/Kconfig +++ b/net/udp/Kconfig @@ -57,18 +57,6 @@ config NET_UDP_READAHEAD select NET_READAHEAD select MM_IOB -config UDP_READAHEAD_NOTIFIER - bool "Support UDP read-ahead notifications" - default n - depends on NET_UDP_READAHEAD && SCHED_WORKQUEUE - select WQUEUE_NOTIFIER - ---help--- - Enable building of UDP read-ahead notifier logic that will execute a - worker function on the high priority work queue when read-ahead data - is available. This is is a general purpose notifier, but was - developed specifically to support poll() logic where the poll must - wait for read-ahead data to become available. - config NET_UDP_WRITE_BUFFERS bool "Enable UDP/IP write buffering" default n @@ -117,5 +105,17 @@ config NET_UDP_WRBUFFER_DUMP endif # NET_UDP_WRITE_BUFFERS +config UDP_NOTIFIER + bool "Support UDP read-ahead notifications" + default n + depends on SCHED_WORKQUEUE + select WQUEUE_NOTIFIER + ---help--- + Enable building of UDP read-ahead notifier logic that will execute a + worker function on the high priority work queue when read-ahead data + is available. This is is a general purpose notifier, but was + developed specifically to support poll() logic where the poll must + wait for read-ahead data to become available. + endif # NET_UDP && !NET_UDP_NO_STACK endmenu # UDP Networking diff --git a/net/udp/Make.defs b/net/udp/Make.defs index 845b9085eb..f7d09fc51b 100644 --- a/net/udp/Make.defs +++ b/net/udp/Make.defs @@ -54,9 +54,12 @@ endif ifeq ($(CONFIG_NET_UDP_READAHEAD),y) SOCK_CSRCS += udp_netpoll.c -ifeq ($(CONFIG_UDP_READAHEAD_NOTIFIER),y) +ifeq ($(CONFIG_UDP_NOTIFIER),y) SOCK_CSRCS += udp_notifier.c endif +ifeq ($(CONFIG_NET_UDP_WRITE_BUFFERS),y) +SOCK_CSRCS += udp_txdrain.c +endif endif # Transport layer diff --git a/net/udp/udp.h b/net/udp/udp.h index 6bf3a9448b..055874598e 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -53,7 +53,7 @@ # include #endif -#ifdef CONFIG_UDP_READAHEAD_NOTIFIER +#ifdef CONFIG_UDP_NOTIFIER # include #endif @@ -671,7 +671,7 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds); #endif /**************************************************************************** - * Name: udp_notifier_setup + * Name: udp_readahead_notifier_setup * * Description: * Set up to perform a callback to the worker function when an UDP data @@ -696,9 +696,42 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds); * ****************************************************************************/ -#ifdef CONFIG_UDP_READAHEAD_NOTIFIER -int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn, - FAR void *arg); +#ifdef CONFIG_UDP_NOTIFIER +int udp_readahead_notifier_setup(worker_t worker, + FAR struct udp_conn_s *conn, + FAR void *arg); +#endif + +/**************************************************************************** + * Name: udp_writebuffer_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function when an UDP write + * buffer is emptied. The worker function will execute on the high + * priority worker thread. + * + * Input Parameters: + * worker - The worker function to execute on the low priority work + * queue when data is available in the UDP read-ahead buffer. + * conn - The UDP connection where read-ahead data is needed. + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * > 0 - The notification is in place. The returned value is a key that + * may be used later in a call to udp_notifier_teardown(). + * == 0 - There is already buffered read-ahead data. No notification + * will be provided. + * < 0 - An unexpected error occurred and no notification will occur. + * The returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_UDP_NOTIFIER +int udp_writebuffer_notifier_setup(worker_t worker, + FAR struct udp_conn_s *conn, + FAR void *arg); #endif /**************************************************************************** @@ -706,13 +739,13 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn, * * Description: * Eliminate a UDP read-ahead notification previously setup by - * udp_notifier_setup(). This function should only be called if the + * udp_readahead_notifier_setup(). This function should only be called if the * notification should be aborted prior to the notification. The * notification will automatically be torn down after the notification. * * Input Parameters: * key - The key value returned from a previous call to - * udp_notifier_setup(). + * udp_readahead_notifier_setup(). * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned on @@ -720,12 +753,12 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn, * ****************************************************************************/ -#ifdef CONFIG_UDP_READAHEAD_NOTIFIER +#ifdef CONFIG_UDP_NOTIFIER int udp_notifier_teardown(int key); #endif /**************************************************************************** - * Name: udp_notifier_signal + * Name: udp_readahead_signal * * Description: * Read-ahead data has been buffered. Notify all threads waiting for @@ -734,7 +767,7 @@ int udp_notifier_teardown(int key); * When read-ahead data becomes available, *all* of the workers waiting * for read-ahead data will be executed. If there are multiple workers * waiting for read-ahead data then only the first to execute will get the - * data. Others will need to call udp_notifier_setup() once again. + * data. Others will need to call udp_readahead_notifier_setup() once again. * * Input Parameters: * conn - The UDP connection where read-ahead data was just buffered. @@ -744,8 +777,57 @@ int udp_notifier_teardown(int key); * ****************************************************************************/ -#ifdef CONFIG_UDP_READAHEAD_NOTIFIER -void udp_notifier_signal(FAR struct udp_conn_s *conn); +#if defined(CONFIG_NET_UDP_READAHEAD) && defined(CONFIG_UDP_NOTIFIER) +void udp_readahead_signal(FAR struct udp_conn_s *conn); +#endif + +/**************************************************************************** + * Name: udp_writebuffer_signal + * + * Description: + * All buffer Tx data has been sent. Signal all threads waiting for the + * write buffers to become empty. + * + * When write buffer becomes empty, *all* of the workers waiting + * for that event data will be executed. If there are multiple workers + * waiting for read-ahead data then only the first to execute will get the + * data. Others will need to call tcp_writebuffer_notifier_setup() once + * again. + * + * Input Parameters: + * conn - The UDP connection where read-ahead data was just buffered. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_UDP_NOTIFIER) +void udp_writebuffer_signal(FAR struct udp_conn_s *conn); +#endif + +/**************************************************************************** + * Name: udp_txdrain + * + * Description: + * Wait for all write buffers to be sent (or for a timeout to occur). + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * abstime - The absolute time when the timeout will occur + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_UDP_NOTIFIER) +struct timespec; +int udp_txdrain(FAR struct socket *psock, + FAR const struct timespec *abstime); +#else +# define udp_txdrain(conn, abstime) (0) #endif #undef EXTERN diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c index 59783b5d3d..743839cb9a 100644 --- a/net/udp/udp_callback.c +++ b/net/udp/udp_callback.c @@ -230,12 +230,12 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, FAR struct udp_con return 0; } -#ifdef CONFIG_UDP_READAHEAD_NOTIFIER +#ifdef CONFIG_UDP_NOTIFIER /* Provided notification(s) that additional UDP read-ahead data is * available. */ - udp_notifier_signal(conn); + udp_readahead_signal(conn); #endif ninfo("Buffered %d bytes\n", buflen); diff --git a/net/udp/udp_notifier.c b/net/udp/udp_notifier.c index 63cfd4e122..a9919cf3f0 100644 --- a/net/udp/udp_notifier.c +++ b/net/udp/udp_notifier.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/udp/udp_notifier.c * - * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Copyright (C) 2018-2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -47,14 +47,14 @@ #include "udp/udp.h" -#ifdef CONFIG_UDP_READAHEAD_NOTIFIER +#ifdef CONFIG_UDP_NOTIFIER /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: udp_notifier_setup + * Name: udp_readahead_notifier_setup * * Description: * Set up to perform a callback to the worker function when an UDP data @@ -79,9 +79,11 @@ * ****************************************************************************/ -int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn, - FAR void *arg) +int udp_readahead_notifier_setup(worker_t worker, + FAR struct udp_conn_s *conn, + FAR void *arg) { +#ifdef CONFIG_NET_UDP_READAHEAD struct work_notifier_s info; DEBUGASSERT(worker != NULL); @@ -104,6 +106,67 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn, info.worker = worker; return work_notifier_setup(&info); +#else + return 0; +#endif +} + +/**************************************************************************** + * Name: udp_writebuffer_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function when an UDP write + * buffer is emptied. The worker function will execute on the high + * priority worker thread. + * + * Input Parameters: + * worker - The worker function to execute on the low priority work + * queue when data is available in the UDP read-ahead buffer. + * conn - The UDP connection where read-ahead data is needed. + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * > 0 - The notification is in place. The returned value is a key that + * may be used later in a call to udp_notifier_teardown(). + * == 0 - There is already buffered read-ahead data. No notification + * will be provided. + * < 0 - An unexpected error occurred and no notification will occur. + * The returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +int udp_writebuffer_notifier_setup(worker_t worker, + FAR struct udp_conn_s *conn, + FAR void *arg) +{ +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS + struct work_notifier_s info; + + DEBUGASSERT(worker != NULL); + + /* If there is already buffered read-ahead data, then return zero without + * setting up the notification. + */ + + if (sq_empty(&conn->write_q)) + { + return 0; + } + + /* Otherwise, this is just a simple wrapper around work_notifer_setup(). */ + + info.evtype = WORK_UDP_WRITEBUFFER; + info.qid = LPWORK; + info.qualifier = conn; + info.arg = arg; + info.worker = worker; + + return work_notifier_setup(&info); +#else + return 0; +#endif } /**************************************************************************** @@ -111,13 +174,13 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn, * * Description: * Eliminate a UDP read-ahead notification previously setup by - * udp_notifier_setup(). This function should only be called if the + * udp_readahead_notifier_setup(). This function should only be called if the * notification should be aborted prior to the notification. The * notification will automatically be torn down after the notification. * * Input Parameters: * key - The key value returned from a previous call to - * udp_notifier_setup(). + * udp_readahead_notifier_setup(). * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned on @@ -133,7 +196,7 @@ int udp_notifier_teardown(int key) } /**************************************************************************** - * Name: udp_notifier_signal + * Name: udp_readahead_signal * * Description: * Read-ahead data has been buffered. Notify all threads waiting for @@ -142,7 +205,7 @@ int udp_notifier_teardown(int key) * When read-ahead data becomes available, *all* of the workers waiting * for read-ahead data will be executed. If there are multiple workers * waiting for read-ahead data then only the first to execute will get the - * data. Others will need to call udp_notifier_setup() once again. + * data. Others will need to call udp_readahead_notifier_setup() once again. * * Input Parameters: * conn - The UDP connection where read-ahead data was just buffered. @@ -152,11 +215,43 @@ int udp_notifier_teardown(int key) * ****************************************************************************/ -void udp_notifier_signal(FAR struct udp_conn_s *conn) +#ifdef CONFIG_NET_UDP_READAHEAD +void udp_readahead_signal(FAR struct udp_conn_s *conn) { /* This is just a simple wrapper around work_notifier_signal(). */ return work_notifier_signal(WORK_UDP_READAHEAD, conn); } +#endif -#endif /* CONFIG_UDP_READAHEAD_NOTIFIER */ +/**************************************************************************** + * Name: udp_writebuffer_signal + * + * Description: + * All buffer Tx data has been sent. Signal all threads waiting for the + * write buffers to become empty. + * + * When write buffer becomes empty, *all* of the workers waiting + * for that event data will be executed. If there are multiple workers + * waiting for read-ahead data then only the first to execute will get the + * data. Others will need to call tcp_writebuffer_notifier_setup() once + * again. + * + * Input Parameters: + * conn - The UDP connection where read-ahead data was just buffered. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +void udp_writebuffer_signal(FAR struct udp_conn_s *conn) +{ + /* This is just a simple wrapper around work_notifier_signal(). */ + + return work_notifier_signal(WORK_UDP_WRITEBUFFER, conn); +} +#endif + +#endif /* CONFIG_UDP_NOTIFIER */ diff --git a/net/udp/udp_psock_sendto_buffered.c b/net/udp/udp_psock_sendto_buffered.c index 5f159d1ecf..c1b200fdda 100644 --- a/net/udp/udp_psock_sendto_buffered.c +++ b/net/udp/udp_psock_sendto_buffered.c @@ -167,6 +167,12 @@ static void sendto_writebuffer_release(FAR struct socket *psock, psock->s_sndcb->priv = NULL; psock->s_sndcb->event = NULL; wrb = NULL; + +#ifdef CONFIG_TCP_NOTIFIER + /* Notify any waiters that the write buffers have been drained. */ + + udp_writebuffer_signal(conn); +#endif } else { diff --git a/net/udp/udp_txdrain.c b/net/udp/udp_txdrain.c new file mode 100644 index 0000000000..d20df793a4 --- /dev/null +++ b/net/udp/udp_txdrain.c @@ -0,0 +1,146 @@ +/**************************************************************************** + * net/udp/udp_txdrain.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udp/udp.h" + +#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_UDP_NOTIFIER) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: txdrain_worker + * + * Description: + * Called with the write buffers have all been sent. + * + * Input Parameters: + * arg - The semaphore that will wake up udp_txdrain + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void txdrain_worker(FAR void *arg) +{ + FAR sem_t *waitsem = (FAR sem_t *)arg; + DEBUGASSERT(waitsem != NULL); + sem_post(waitsem); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: udp_txdrain + * + * Description: + * Wait for all write buffers to be sent (or for a timeout to occur). + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * abstime - The absolute time when the timeout will occur + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +int udp_txdrain(FAR struct socket *psock, + FAR const struct timespec *abstime) +{ + FAR struct udp_conn_s *conn; + sem_t waitsem; + int ret; + + DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && psock->s_conn != NULL); + DEBUGASSERT(psock->s_type == SOCK_DGRAM); + + conn = (FAR struct udp_conn_s *)psock->s_conn; + + /* Initialize the wait semaphore */ + + nxsem_init(&waitsem, 0, 0); + + /* The following needs to be done with the network stable */ + + net_lock(); + ret = udp_writebuffer_notifier_setup(txdrain_worker, conn, &waitsem); + if (ret > 0) + { + int key = ret; + + /* There is pending write data.. wait for it to drain. */ + + do + { + ret = net_timedwait(&waitsem, abstime); + } + while (ret == EINTR); + + /* Tear down the notifier (in case we timed out or were canceled) */ + + if (ret < 0) + { + udp_notifier_teardown(key); + } + } + + net_unlock(); + nxsem_destroy(&waitsem); + return ret; +} + +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS && CONFIG_UDP_NOTIFIER */