[PATCH BlueZ 1/2] shared/io: add helper for asynchronous shutdown() with SO_LINGER

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add io_glib_shutdown_linger() for socket shutdown with wait for
remote ACK via SO_LINGER. E.g. wait for L2CAP Disconnect Rsp.

We don't want to block the main loop for the linger timeout, so call
shutdown() in a separate thread, as socket API seems to provide only the
blocking way to do it.

Implement it with Glib source API, as that's more convenient for the
AVDTP plugin that needs this.
---
 src/shared/io-ell.c      |  6 +++
 src/shared/io-glib.c     | 89 ++++++++++++++++++++++++++++++++++++++++
 src/shared/io-mainloop.c |  6 +++
 src/shared/io.h          |  3 ++
 4 files changed, 104 insertions(+)

diff --git a/src/shared/io-ell.c b/src/shared/io-ell.c
index 4d64cf3c5..e7baed0d3 100644
--- a/src/shared/io-ell.c
+++ b/src/shared/io-ell.c
@@ -315,3 +315,9 @@ unsigned int io_glib_add_err_watch(void *giochannel, io_glib_err_func_t func,
 {
 	return 0;
 }
+
+unsigned int io_glib_shutdown_linger(void *giochannel, int how, int timeout,
+				io_destroy_func_t func, void *user_data)
+{
+	return 0;
+}
diff --git a/src/shared/io-glib.c b/src/shared/io-glib.c
index 81cd1122b..efb7b9f5c 100644
--- a/src/shared/io-glib.c
+++ b/src/shared/io-glib.c
@@ -461,3 +461,92 @@ unsigned int io_glib_add_err_watch(void *giochannel,
 					G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 					err_watch_callback, data, g_free);
 }
+
+/*
+ * shutdown() socket, enabling SO_LINGER to wait for close ACK, with
+ * asynchronous callback.
+ */
+
+struct shutdown_linger {
+	GSource			source;
+	io_destroy_func_t	func;
+	void			*user_data;
+	int			how;
+	GIOChannel		*io;
+	GThread			*thread;
+};
+
+static gpointer shutdown_linger_thread(gpointer data)
+{
+	struct shutdown_linger *source = data;
+
+	shutdown(g_io_channel_unix_get_fd(source->io), source->how);
+	g_source_set_ready_time(&source->source, 0);
+	g_source_unref(&source->source);
+	return NULL;
+}
+
+static gboolean shutdown_linger_dispatch(GSource *gsource, GSourceFunc callback,
+							gpointer user_data)
+{
+	struct shutdown_linger *source = (void *)gsource;
+
+	if (source->func)
+		source->func(source->user_data);
+	return FALSE;
+}
+
+static void shutdown_linger_finalize(GSource *gsource)
+{
+	struct shutdown_linger *source = (void *)gsource;
+
+	if (source->thread == g_thread_self())
+		g_thread_unref(source->thread);
+	else
+		g_thread_join(source->thread);
+
+	g_io_channel_unref(source->io);
+}
+
+unsigned int io_glib_shutdown_linger(void *giochannel, int how, int timeout,
+				io_destroy_func_t func, void *user_data)
+{
+	static GSourceFuncs source_funcs = {
+		.dispatch = shutdown_linger_dispatch,
+		.finalize = shutdown_linger_finalize,
+	};
+	struct linger linger = {
+		.l_onoff = 1,
+		.l_linger = timeout,
+	};
+	GIOChannel *io = giochannel;
+	struct shutdown_linger *source;
+	guint id;
+	int fd;
+
+	if (!io)
+		return 0;
+
+	fd = g_io_channel_unix_get_fd(io);
+	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger))) {
+		shutdown(fd, how);
+		return 0;
+	}
+
+	source = (void *)g_source_new(&source_funcs, sizeof(*source));
+
+	g_source_set_name(&source->source, "shutdown_linger");
+	source->func = func;
+	source->user_data = user_data;
+	source->how = how;
+	source->io = g_io_channel_ref(io);
+
+	g_source_ref(&source->source);  /* unref in thread */
+	source->thread = g_thread_new("shutdown_linger", shutdown_linger_thread,
+								source);
+
+	id = g_source_attach(&source->source, NULL);
+	g_source_unref(&source->source);
+
+	return id;
+}
diff --git a/src/shared/io-mainloop.c b/src/shared/io-mainloop.c
index 8fd49935e..abe76de1d 100644
--- a/src/shared/io-mainloop.c
+++ b/src/shared/io-mainloop.c
@@ -321,3 +321,9 @@ unsigned int io_glib_add_err_watch(void *giochannel, io_glib_err_func_t func,
 {
 	return 0;
 }
+
+unsigned int io_glib_shutdown_linger(void *giochannel, int how, int timeout,
+				io_destroy_func_t func, void *user_data)
+{
+	return 0;
+}
diff --git a/src/shared/io.h b/src/shared/io.h
index 87c3c001c..7909b1707 100644
--- a/src/shared/io.h
+++ b/src/shared/io.h
@@ -37,3 +37,6 @@ bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
 typedef void (*io_glib_err_func_t)(int cond, void *user_data);
 unsigned int io_glib_add_err_watch(void *giochannel, io_glib_err_func_t func,
 							void *user_data);
+
+unsigned int io_glib_shutdown_linger(void *giochannel, int how, int timeout,
+				io_destroy_func_t func, void *user_data);
-- 
2.51.0





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux