diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 39444f79d..7936476ce 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2225,6 +2225,7 @@ g_socket_control_message_get_type gsocketlistener GSocketListener GSocketListener +GSocketListenerEvent g_socket_listener_new g_socket_listener_add_socket g_socket_listener_add_address diff --git a/gio/gioenums.h b/gio/gioenums.h index 9fd563dcc..55a70b174 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1750,6 +1750,29 @@ typedef enum { G_SOCKET_CLIENT_COMPLETE } GSocketClientEvent; +/** + * GSocketListenerEvent: + * @G_SOCKET_LISTENER_BINDING: The listener is about to bind a socket. + * @G_SOCKET_LISTENER_BOUND: The listener has bound a socket. + * @G_SOCKET_LISTENER_LISTENING: The listener is about to start + * listening on this socket. + * @G_SOCKET_LISTENER_LISTENED: The listener is now listening on + * this socket. + * + * Describes an event occurring on a #GSocketListener. See the + * #GSocketListener::event signal for more details. + * + * Additional values may be added to this type in the future. + * + * Since: 2.46 + */ +typedef enum { + G_SOCKET_LISTENER_BINDING, + G_SOCKET_LISTENER_BOUND, + G_SOCKET_LISTENER_LISTENING, + G_SOCKET_LISTENER_LISTENED +} GSocketListenerEvent; + /** * GTestDBusFlags: * @G_TEST_DBUS_NONE: No flags. diff --git a/gio/gsocketlistener.c b/gio/gsocketlistener.c index 7185745ff..8b60ea316 100644 --- a/gio/gsocketlistener.c +++ b/gio/gsocketlistener.c @@ -26,6 +26,7 @@ #include "config.h" #include "gsocketlistener.h" +#include #include #include #include @@ -61,6 +62,13 @@ enum PROP_LISTEN_BACKLOG }; +enum +{ + EVENT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; static GQuark source_quark = 0; @@ -131,7 +139,6 @@ g_socket_listener_set_property (GObject *object, } } - static void g_socket_listener_class_init (GSocketListenerClass *klass) { @@ -149,6 +156,29 @@ g_socket_listener_class_init (GSocketListenerClass *klass) 10, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GSocketListener::event: + * @listener: the #GSocketListener + * @event: the event that is occurring + * @socket: the #GSocket the event is occurring on + * + * Emitted when @listener's activity on @socket changes state. + * Note that when @listener is used to listen on both IPv4 and + * IPv6, a separate set of signals will be emitted for each, and + * the order they happen in is undefined. + * + * Since: 2.46 + */ + signals[EVENT] = + g_signal_new (I_("event"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSocketListenerClass, event), + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_SOCKET_LISTENER_EVENT, + G_TYPE_SOCKET); + source_quark = g_quark_from_static_string ("g-socket-listener-source"); } @@ -306,13 +336,29 @@ g_socket_listener_add_address (GSocketListener *listener, g_socket_set_listen_backlog (socket, listener->priv->listen_backlog); - if (!g_socket_bind (socket, address, TRUE, error) || - !g_socket_listen (socket, error)) + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BINDING, socket); + + if (!g_socket_bind (socket, address, TRUE, error)) { g_object_unref (socket); return FALSE; } + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BOUND, socket); + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENING, socket); + + if (!g_socket_listen (socket, error)) + { + g_object_unref (socket); + return FALSE; + } + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENED, socket); + local_address = NULL; if (effective_address) { @@ -392,7 +438,6 @@ g_socket_listener_add_inet_port (GSocketListener *listener, { GInetAddress *inet_address; GSocketAddress *address; - gboolean result; inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); address = g_inet_socket_address_new (inet_address, port); @@ -400,18 +445,32 @@ g_socket_listener_add_inet_port (GSocketListener *listener, g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog); - result = g_socket_bind (socket6, address, TRUE, error) && - g_socket_listen (socket6, error); + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BINDING, socket6); + + if (!g_socket_bind (socket6, address, TRUE, error)) + { + g_object_unref (address); + g_object_unref (socket6); + return FALSE; + } g_object_unref (address); - if (!result) + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BOUND, socket6); + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENING, socket6); + + if (!g_socket_listen (socket6, error)) { g_object_unref (socket6); - return FALSE; } + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENED, socket6); + if (source_object) g_object_set_qdata_full (G_OBJECT (socket6), source_quark, g_object_ref (source_object), @@ -445,7 +504,6 @@ g_socket_listener_add_inet_port (GSocketListener *listener, { GInetAddress *inet_address; GSocketAddress *address; - gboolean result; inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); address = g_inet_socket_address_new (inet_address, port); @@ -454,21 +512,40 @@ g_socket_listener_add_inet_port (GSocketListener *listener, g_socket_set_listen_backlog (socket4, listener->priv->listen_backlog); - result = g_socket_bind (socket4, address, TRUE, error) && - g_socket_listen (socket4, error); + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BINDING, socket4); - g_object_unref (address); - - if (!result) + if (!g_socket_bind (socket4, address, TRUE, error)) { + g_object_unref (address); g_object_unref (socket4); - if (socket6 != NULL) g_object_unref (socket6); return FALSE; } + g_object_unref (address); + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BOUND, socket4); + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENING, socket4); + + if (!g_socket_listen (socket4, error)) + { + g_object_unref (socket4); + if (socket6 != NULL) + g_object_unref (socket6); + + return FALSE; + } + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENED, socket4); + + g_object_unref (address); + if (source_object) g_object_set_qdata_full (G_OBJECT (socket4), source_quark, g_object_ref (source_object), @@ -965,6 +1042,10 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); address = g_inet_socket_address_new (inet_address, 0); g_object_unref (inet_address); + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BINDING, socket6); + result = g_socket_bind (socket6, address, TRUE, error); g_object_unref (address); @@ -976,6 +1057,9 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, break; } + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BOUND, socket6); + g_assert (G_IS_INET_SOCKET_ADDRESS (address)); candidate_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); @@ -1004,6 +1088,10 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); address = g_inet_socket_address_new (inet_address, candidate_port); g_object_unref (inet_address); + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BINDING, socket4); + /* a note on the 'error' clause below: * * if candidate_port is 0 then we report the error right away @@ -1029,8 +1117,11 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, if (result) /* got our candidate port successfully */ - break; - + { + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BOUND, socket4); + break; + } else /* we failed to bind to the specified port. try again. */ { @@ -1060,6 +1151,9 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, break; } + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_BOUND, socket4); + g_assert (G_IS_INET_SOCKET_ADDRESS (address)); candidate_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); @@ -1083,6 +1177,10 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, if (socket6 != NULL) { g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog); + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENING, socket6); + if (!g_socket_listen (socket6, error)) { g_object_unref (socket6); @@ -1092,6 +1190,9 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, return 0; } + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENED, socket6); + if (source_object) g_object_set_qdata_full (G_OBJECT (socket6), source_quark, g_object_ref (source_object), @@ -1103,6 +1204,10 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, if (socket4 != NULL) { g_socket_set_listen_backlog (socket4, listener->priv->listen_backlog); + + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENING, socket4); + if (!g_socket_listen (socket4, error)) { g_object_unref (socket4); @@ -1112,6 +1217,9 @@ g_socket_listener_add_any_inet_port (GSocketListener *listener, return 0; } + g_signal_emit (listener, signals[EVENT], 0, + G_SOCKET_LISTENER_LISTENED, socket4); + if (source_object) g_object_set_qdata_full (G_OBJECT (socket4), source_quark, g_object_ref (source_object), diff --git a/gio/gsocketlistener.h b/gio/gsocketlistener.h index 82d309acb..abf064a06 100644 --- a/gio/gsocketlistener.h +++ b/gio/gsocketlistener.h @@ -61,8 +61,11 @@ struct _GSocketListenerClass void (* changed) (GSocketListener *listener); + void (* event) (GSocketListener *listener, + GSocketListenerEvent *event, + GSocket *socket); + /* Padding for future expansion */ - void (*_g_reserved1) (void); void (*_g_reserved2) (void); void (*_g_reserved3) (void); void (*_g_reserved4) (void); diff --git a/gio/tests/socket-listener.c b/gio/tests/socket-listener.c index 98e33960d..5a58206bc 100644 --- a/gio/tests/socket-listener.c +++ b/gio/tests/socket-listener.c @@ -19,6 +19,70 @@ #include +static void +event_cb (GSocketListener *listener, + GSocketListenerEvent event, + GSocket *socket, + gpointer data) +{ + static GSocketListenerEvent expected_event = G_SOCKET_LISTENER_BINDING; + gboolean *success = (gboolean *)data; + + g_assert (G_IS_SOCKET_LISTENER (listener)); + g_assert (G_IS_SOCKET (socket)); + g_assert (event == expected_event); + + switch (event) + { + case G_SOCKET_LISTENER_BINDING: + expected_event = G_SOCKET_LISTENER_BOUND; + break; + case G_SOCKET_LISTENER_BOUND: + expected_event = G_SOCKET_LISTENER_LISTENING; + break; + case G_SOCKET_LISTENER_LISTENING: + expected_event = G_SOCKET_LISTENER_LISTENED; + break; + case G_SOCKET_LISTENER_LISTENED: + *success = TRUE; + break; + } +} + +static void +test_event_signal (void) +{ + gboolean success = FALSE; + GInetAddress *iaddr; + GSocketAddress *saddr; + GSocketListener *listener; + GError *error = NULL; + + iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); + saddr = g_inet_socket_address_new (iaddr, 0); + g_object_unref (iaddr); + + listener = g_socket_listener_new (); + + g_signal_connect (listener, "event", G_CALLBACK (event_cb), &success); + + g_socket_listener_add_address (listener, + saddr, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_TCP, + NULL, + NULL, + &error); + g_assert_no_error (error); + g_object_unref (saddr); + + do + g_main_context_iteration (NULL, TRUE); + while (!success); + + g_object_unref (listener); +} + GMutex mutex_712570; GCond cond_712570; volatile gboolean finalized; @@ -160,6 +224,7 @@ main (int argc, g_test_bug_base ("http://bugzilla.gnome.org/"); + g_test_add_func ("/socket-listener/event-signal", test_event_signal); g_test_add_func ("/socket-listener/threaded/712570", test_threaded_712570); return g_test_run();