]> git.ozlabs.org Git - ccan/commitdiff
io: add new io_sock_shutdown helper.
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 4 Jan 2022 02:36:34 +0000 (13:06 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 4 Jan 2022 05:53:12 +0000 (16:23 +1030)
Turns out that there's no good way to flush a socket:

https://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/io/io.c
ccan/io/io.h

index 36dcb81e720f448bf46665f93bb40f0692d2256c..12df06d82cb6474fed825cd381f7f225fc6213b1 100644 (file)
@@ -529,6 +529,18 @@ bool io_plan_out_started(const struct io_conn *conn)
        return conn->plan[IO_OUT].status == IO_POLLING_STARTED;
 }
 
+/* Despite being a TCP expert, I missed the full extent of this
+ * problem.  The legendary ZmnSCPxj implemented it (with the URL
+ * pointing to the explanation), and I imitate that here. */
+struct io_plan *io_sock_shutdown(struct io_conn *conn)
+{
+       if (shutdown(io_conn_fd(conn), SHUT_WR) != 0)
+               return io_close(conn);
+
+       /* And leave unset .*/
+       return &conn->plan[IO_IN];
+}
+       
 bool io_flush_sync(struct io_conn *conn)
 {
        struct io_plan *plan = &conn->plan[IO_OUT];
index e6905fb9241a78e10b56a5b63b432a99597ee185..1197626f12671a80970b406ce52477bb1c6effed 100644 (file)
@@ -389,6 +389,40 @@ struct io_plan *io_out_always_(struct io_conn *conn,
                                                       void *),
                               void *arg);
 
+/**
+ * io_sock_shutdown - start socket close process (flushes TCP sockets).
+ * @conn: the connection the plan is for
+ *
+ * Simply closing a TCP socket can lose data; unfortunately you should
+ * shutdown(SHUT_WR) and wait for the other side to see this and close.
+ * Of course, you also need to set a timer, in case it doesn't (you may
+ * already have some responsiveness timer, of course).
+ *
+ * On error, is equivalent to io_close().
+ *
+ * Example:
+ * #include <ccan/timer/timer.h>
+ *
+ * // Timer infra needs wrapper to contain extra data.
+ * struct timeout_timer {
+ *    struct timer t;
+ *    struct io_conn *conn;
+ * };
+ * static struct timers timers;
+ *
+ * static struct io_plan *flush_and_close(struct io_conn *conn)
+ * {
+ *    struct timeout_timer *timeout;
+ *    // Freed if conn closes normally.
+ *    timeout = tal(conn, struct timeout_timer);
+ *    timeout->conn = conn;
+ *    timeout->t = conn;
+ *    timer_addrel(&timers, &timeout->t, time_from_sec(5));
+ *    return io_sock_shutdown(conn);
+ * }
+ */
+struct io_plan *io_sock_shutdown(struct io_conn *conn);
+
 /**
  * io_connect - create an asynchronous connection to a listening socket.
  * @conn: the connection that plan is for.