@@ -253,6 +253,7 @@ shared_sources += src/shared/shell.c src/shared/shell.h
endif
src_libshared_glib_la_SOURCES = $(shared_sources) \
+ src/shared/io-glib.h \
src/shared/io-glib.c \
src/shared/timeout-glib.c \
src/shared/mainloop-glib.c \
@@ -63,8 +63,7 @@ AC_DEFUN([COMPILER_FLAGS], [
with_cflags="$with_cflags -Wformat -Wformat-security"
with_cflags="$with_cflags -Wstringop-overflow"
with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
- with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
- with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32"
+ with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36"
fi
AC_SUBST([WARNING_CFLAGS], $with_cflags)
])
@@ -81,7 +81,7 @@ AC_CHECK_DECLS([basename], [],
])
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28)
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.36)
if (test "${enable_threads}" = "yes"); then
AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
@@ -13,10 +13,14 @@
#endif
#include <errno.h>
+#include <sys/socket.h>
#include <glib.h>
#include "src/shared/io.h"
+#include "src/shared/io-glib.h"
+
+#define IO_ERR_WATCH_RATELIMIT (500 * G_TIME_SPAN_MILLISECOND)
struct io_watch {
struct io *io;
@@ -29,11 +33,19 @@ struct io_watch {
struct io {
int ref_count;
GIOChannel *channel;
+ bool err_watch;
struct io_watch *read_watch;
struct io_watch *write_watch;
struct io_watch *disconnect_watch;
};
+struct io_err_watch {
+ GSource source;
+ GIOChannel *io;
+ GIOCondition events;
+ gpointer tag;
+};
+
static struct io *io_ref(struct io *io)
{
if (!io)
@@ -179,10 +191,17 @@ static struct io_watch *watch_new(struct io *io, GIOCondition cond,
prio = cond == G_IO_HUP ? G_PRIORITY_DEFAULT_IDLE : G_PRIORITY_DEFAULT;
- watch->id = g_io_add_watch_full(io->channel, prio,
+ if (!io->err_watch)
+ watch->id = g_io_add_watch_full(io->channel, prio,
+ cond | G_IO_ERR | G_IO_NVAL,
+ watch_callback, watch,
+ watch_destroy);
+ else
+ watch->id = io_glib_add_err_watch_full(io->channel, prio,
cond | G_IO_ERR | G_IO_NVAL,
watch_callback, watch,
watch_destroy);
+
if (watch->id == 0) {
watch_destroy(watch);
return NULL;
@@ -250,6 +269,15 @@ bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
return io_set_handler(io, G_IO_HUP, callback, user_data, destroy);
}
+bool io_set_use_err_watch(struct io *io, bool err_watch)
+{
+ if (!io)
+ return false;
+
+ io->err_watch = err_watch;
+ return true;
+}
+
ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
{
int fd;
@@ -278,3 +306,130 @@ bool io_shutdown(struct io *io)
return g_io_channel_shutdown(io->channel, TRUE, NULL)
== G_IO_STATUS_NORMAL;
}
+
+/*
+ * GSource implementation that tolerates non-empty MSG_ERRQUEUE, without
+ * attempting to flush it. This is intended for use with TX timestamping in
+ * cases where someone else is reading the timestamps and we are only interested
+ * in POLLHUP or socket errors.
+ */
+
+static gint64 io_err_watch_wakeup;
+
+static gboolean io_err_watch_dispatch(GSource *source,
+ GSourceFunc callback, gpointer user_data)
+{
+ struct io_err_watch *watch = (void *)source;
+ const GIOFunc func = (void *)callback;
+ const gint64 timeout = IO_ERR_WATCH_RATELIMIT;
+ GIOCondition cond;
+ int fd;
+
+ if (!func)
+ return FALSE;
+
+ fd = g_io_channel_unix_get_fd(watch->io);
+
+ /*
+ * If woken up by POLLERR only, and SO_ERROR is not set, ignore this
+ * event. Also disable polling for some time so that we don't consume
+ * too much CPU on events we are not interested in, or busy loop if
+ * nobody is flushing the errqueue.
+ */
+
+ if (watch->tag)
+ cond = g_source_query_unix_fd(&watch->source, watch->tag);
+ else
+ cond = 0;
+
+ if (cond == G_IO_ERR) {
+ int err, ret;
+ socklen_t len = sizeof(err);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
+ if (ret == 0 && err == 0) {
+ g_source_remove_unix_fd(&watch->source, watch->tag);
+ watch->tag = NULL;
+
+ /* io_err watches all wake up at the same time */
+ if (!io_err_watch_wakeup)
+ io_err_watch_wakeup = g_get_monotonic_time()
+ + timeout;
+
+ g_source_set_ready_time(&watch->source,
+ io_err_watch_wakeup);
+ return TRUE;
+ }
+ }
+
+ if (g_source_get_ready_time(&watch->source) != -1) {
+ g_assert(!watch->tag);
+ io_err_watch_wakeup = 0;
+ watch->tag = g_source_add_unix_fd(&watch->source, fd,
+ watch->events);
+ g_source_set_ready_time(&watch->source, -1);
+ }
+
+ cond &= watch->events;
+
+ if (cond)
+ return func(watch->io, cond, user_data);
+ else
+ return TRUE;
+}
+
+static void io_err_watch_finalize(GSource *source)
+{
+ struct io_err_watch *watch = (void *)source;
+
+ if (watch->tag)
+ g_source_remove_unix_fd(&watch->source, watch->tag);
+
+ g_io_channel_unref(watch->io);
+}
+
+guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
+ GIOCondition events,
+ GIOFunc func, gpointer user_data,
+ GDestroyNotify notify)
+{
+ static GSourceFuncs source_funcs = {
+ .dispatch = io_err_watch_dispatch,
+ .finalize = io_err_watch_finalize,
+ };
+ GSourceFunc callback = (void *)func;
+ struct io_err_watch *watch;
+ gint fd;
+ guint id;
+
+ g_return_val_if_fail(!(events & (G_IO_IN | G_IO_OUT)), 0);
+ g_return_val_if_fail(events, 0);
+ g_return_val_if_fail(func, 0);
+
+ fd = g_io_channel_unix_get_fd(io);
+
+ watch = (void *)g_source_new(&source_funcs,
+ sizeof(struct io_err_watch));
+
+ watch->io = g_io_channel_ref(io);
+ watch->events = events;
+ watch->tag = g_source_add_unix_fd(&watch->source, fd, events);
+
+ g_source_set_name((void *)watch, "io_glib_err_watch");
+ g_source_set_callback(&watch->source, callback, user_data, notify);
+
+ if (priority != G_PRIORITY_DEFAULT)
+ g_source_set_priority(&watch->source, priority);
+
+ id = g_source_attach(&watch->source, NULL);
+ g_source_unref(&watch->source);
+
+ return id;
+}
+
+guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events, GIOFunc func,
+ gpointer user_data)
+{
+ return io_glib_add_err_watch_full(io, G_PRIORITY_DEFAULT, events,
+ func, user_data, NULL);
+}
new file mode 100644
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#include <glib.h>
+
+guint io_glib_add_err_watch(GIOChannel *io, GIOCondition events,
+ GIOFunc func, gpointer user_data);
+guint io_glib_add_err_watch_full(GIOChannel *io, gint priority,
+ GIOCondition events, GIOFunc func,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+bool io_set_use_err_watch(struct io *io, bool err_watch);