Add an event signal to GSocketListener

This allows the caller to know when a socket has been bound so that
it can for instance set the SO_SENDBUF and SO_RECVBUF socket options
before listen is called

https://bugzilla.gnome.org/show_bug.cgi?id=738207
This commit is contained in:
Paolo Borelli 2014-10-09 15:54:43 +02:00
parent ec9c248d7d
commit b64e2956f6
5 changed files with 218 additions and 18 deletions

View File

@ -2225,6 +2225,7 @@ g_socket_control_message_get_type
<FILE>gsocketlistener</FILE>
<TITLE>GSocketListener</TITLE>
GSocketListener
GSocketListenerEvent
g_socket_listener_new
g_socket_listener_add_socket
g_socket_listener_add_address

View File

@ -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.

View File

@ -26,6 +26,7 @@
#include "config.h"
#include "gsocketlistener.h"
#include <gio/gioenumtypes.h>
#include <gio/gtask.h>
#include <gio/gcancellable.h>
#include <gio/gsocketaddress.h>
@ -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),

View File

@ -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);

View File

@ -19,6 +19,70 @@
#include <gio/gio.h>
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();