2009-05-15 21:26:24 +02:00
|
|
|
|
/* GIO - GLib Input, Output and Streaming Library
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima
|
|
|
|
|
* Copyright © 2009 codethink
|
|
|
|
|
* Copyright © 2009 Red Hat, Inc
|
|
|
|
|
*
|
2022-05-18 09:12:45 +01:00
|
|
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
2017-05-27 18:21:30 +02:00
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General
|
2014-01-23 12:58:29 +01:00
|
|
|
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Authors: Christian Kellner <gicmo@gnome.org>
|
|
|
|
|
* Samuel Cormier-Iijima <sciyoshi@gmail.com>
|
|
|
|
|
* Ryan Lortie <desrt@desrt.ca>
|
|
|
|
|
* Alexander Larsson <alexl@redhat.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "gsocketlistener.h"
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
#include <gio/gioenumtypes.h>
|
2012-08-02 15:48:22 -04:00
|
|
|
|
#include <gio/gtask.h>
|
2009-05-15 21:26:24 +02:00
|
|
|
|
#include <gio/gcancellable.h>
|
|
|
|
|
#include <gio/gsocketaddress.h>
|
|
|
|
|
#include <gio/ginetaddress.h>
|
|
|
|
|
#include <gio/gioerror.h>
|
|
|
|
|
#include <gio/gsocket.h>
|
|
|
|
|
#include <gio/gsocketconnection.h>
|
|
|
|
|
#include <gio/ginetsocketaddress.h>
|
|
|
|
|
#include "glibintl.h"
|
2019-05-30 19:29:18 -07:00
|
|
|
|
#include "gmarshal-internal.h"
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-11-02 16:23:54 +00:00
|
|
|
|
* GSocketListener:
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
2023-11-02 16:23:54 +00:00
|
|
|
|
* A `GSocketListener` is an object that keeps track of a set
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* of server sockets and helps you accept sockets from any of the
|
|
|
|
|
* socket, either sync or async.
|
|
|
|
|
*
|
2023-11-02 16:23:54 +00:00
|
|
|
|
* Add addresses and ports to listen on using
|
|
|
|
|
* [method@Gio.SocketListener.add_address] and
|
|
|
|
|
* [method@Gio.SocketListener.add_inet_port]. These will be listened on until
|
|
|
|
|
* [method@Gio.SocketListener.close] is called. Dropping your final reference to
|
|
|
|
|
* the `GSocketListener` will not cause [method@Gio.SocketListener.close] to be
|
|
|
|
|
* called implicitly, as some references to the `GSocketListener` may be held
|
2018-03-13 14:03:24 +00:00
|
|
|
|
* internally.
|
|
|
|
|
*
|
2023-11-02 16:23:54 +00:00
|
|
|
|
* If you want to implement a network server, also look at
|
|
|
|
|
* [class@Gio.SocketService] and [class@Gio.ThreadedSocketService] which are
|
|
|
|
|
* subclasses of `GSocketListener` that make this even easier.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
PROP_0,
|
|
|
|
|
PROP_LISTEN_BACKLOG
|
|
|
|
|
};
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
EVENT,
|
|
|
|
|
LAST_SIGNAL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
static GQuark source_quark = 0;
|
|
|
|
|
|
|
|
|
|
struct _GSocketListenerPrivate
|
|
|
|
|
{
|
|
|
|
|
GPtrArray *sockets;
|
|
|
|
|
GMainContext *main_context;
|
|
|
|
|
int listen_backlog;
|
|
|
|
|
guint closed : 1;
|
|
|
|
|
};
|
|
|
|
|
|
2013-06-11 00:29:58 +01:00
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GSocketListener, g_socket_listener, G_TYPE_OBJECT)
|
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
static void
|
|
|
|
|
g_socket_listener_finalize (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GSocketListener *listener = G_SOCKET_LISTENER (object);
|
|
|
|
|
|
|
|
|
|
if (listener->priv->main_context)
|
|
|
|
|
g_main_context_unref (listener->priv->main_context);
|
|
|
|
|
|
2014-06-23 15:01:28 +01:00
|
|
|
|
/* Do not explicitly close the sockets. Instead, let them close themselves if
|
|
|
|
|
* their final reference is dropped, but keep them open if a reference is
|
|
|
|
|
* held externally to the GSocketListener (which is possible if
|
|
|
|
|
* g_socket_listener_add_socket() was used).
|
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
g_ptr_array_free (listener->priv->sockets, TRUE);
|
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (g_socket_listener_parent_class)
|
|
|
|
|
->finalize (object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_socket_listener_get_property (GObject *object,
|
|
|
|
|
guint prop_id,
|
|
|
|
|
GValue *value,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
|
|
|
|
GSocketListener *listener = G_SOCKET_LISTENER (object);
|
|
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
|
{
|
|
|
|
|
case PROP_LISTEN_BACKLOG:
|
|
|
|
|
g_value_set_int (value, listener->priv->listen_backlog);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_socket_listener_set_property (GObject *object,
|
|
|
|
|
guint prop_id,
|
|
|
|
|
const GValue *value,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
|
|
|
|
GSocketListener *listener = G_SOCKET_LISTENER (object);
|
|
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
|
{
|
|
|
|
|
case PROP_LISTEN_BACKLOG:
|
|
|
|
|
g_socket_listener_set_backlog (listener, g_value_get_int (value));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_socket_listener_class_init (GSocketListenerClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
|
|
gobject_class->finalize = g_socket_listener_finalize;
|
|
|
|
|
gobject_class->set_property = g_socket_listener_set_property;
|
|
|
|
|
gobject_class->get_property = g_socket_listener_get_property;
|
2023-11-29 13:23:09 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GSocketListener:listen-backlog:
|
|
|
|
|
*
|
|
|
|
|
* The number of outstanding connections in the listen queue.
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
g_object_class_install_property (gobject_class, PROP_LISTEN_BACKLOG,
|
2023-04-28 01:59:26 +02:00
|
|
|
|
g_param_spec_int ("listen-backlog", NULL, NULL,
|
2009-05-15 21:26:24 +02:00
|
|
|
|
0,
|
|
|
|
|
2000,
|
|
|
|
|
10,
|
|
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
/**
|
|
|
|
|
* 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),
|
2019-05-30 19:29:18 -07:00
|
|
|
|
NULL, NULL,
|
|
|
|
|
_g_cclosure_marshal_VOID__ENUM_OBJECT,
|
2014-10-09 15:54:43 +02:00
|
|
|
|
G_TYPE_NONE, 2,
|
|
|
|
|
G_TYPE_SOCKET_LISTENER_EVENT,
|
|
|
|
|
G_TYPE_SOCKET);
|
2019-05-30 19:29:18 -07:00
|
|
|
|
g_signal_set_va_marshaller (signals[EVENT],
|
|
|
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
|
|
|
_g_cclosure_marshal_VOID__ENUM_OBJECTv);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
source_quark = g_quark_from_static_string ("g-socket-listener-source");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_socket_listener_init (GSocketListener *listener)
|
|
|
|
|
{
|
2013-06-24 15:43:04 +01:00
|
|
|
|
listener->priv = g_socket_listener_get_instance_private (listener);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
listener->priv->sockets =
|
|
|
|
|
g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
|
|
|
listener->priv->listen_backlog = 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2009-05-18 13:02:11 +02:00
|
|
|
|
* g_socket_listener_new:
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Creates a new #GSocketListener with no sockets to listen for.
|
|
|
|
|
* New listeners can be added with e.g. g_socket_listener_add_address()
|
|
|
|
|
* or g_socket_listener_add_inet_port().
|
|
|
|
|
*
|
|
|
|
|
* Returns: a new #GSocketListener.
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GSocketListener *
|
|
|
|
|
g_socket_listener_new (void)
|
|
|
|
|
{
|
|
|
|
|
return g_object_new (G_TYPE_SOCKET_LISTENER, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
check_listener (GSocketListener *listener,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
if (listener->priv->closed)
|
|
|
|
|
{
|
|
|
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
|
|
|
|
|
_("Listener is already closed"));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 15:31:53 +01:00
|
|
|
|
static void
|
|
|
|
|
add_socket (GSocketListener *listener,
|
|
|
|
|
GSocket *socket,
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
GObject *source_object,
|
|
|
|
|
gboolean set_non_blocking)
|
2025-07-25 15:31:53 +01:00
|
|
|
|
{
|
|
|
|
|
if (source_object)
|
|
|
|
|
g_object_set_qdata_full (G_OBJECT (socket), source_quark,
|
|
|
|
|
g_object_ref (source_object),
|
|
|
|
|
g_object_unref);
|
|
|
|
|
|
|
|
|
|
g_ptr_array_add (listener->priv->sockets, g_object_ref (socket));
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
|
|
|
|
|
/* Because the implementation of `GSocketListener` uses polling and `GSource`s
|
|
|
|
|
* to wait for connections, we absolutely do *not* need `GSocket`’s internal
|
|
|
|
|
* implementation of blocking operations to get in the way. Otherwise we end
|
|
|
|
|
* up calling poll() on the results of poll(), which is racy and confusing.
|
|
|
|
|
*
|
|
|
|
|
* Unfortunately, the existence of g_socket_listener_add_socket() to add a
|
|
|
|
|
* socket which is used elsewhere, means that we need an escape hatch
|
|
|
|
|
* (`!set_non_blocking`) to allow sockets to remain in blocking mode if the
|
|
|
|
|
* caller really wants it. */
|
|
|
|
|
if (set_non_blocking)
|
|
|
|
|
g_socket_set_blocking (socket, FALSE);
|
2025-07-25 15:31:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_add_socket:
|
|
|
|
|
* @listener: a #GSocketListener
|
2009-06-18 15:28:41 +02:00
|
|
|
|
* @socket: a listening #GSocket
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @source_object: (nullable): Optional #GObject identifying this source
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @error: #GError for error reporting, or %NULL to ignore.
|
|
|
|
|
*
|
|
|
|
|
* Adds @socket to the set of sockets that we try to accept
|
|
|
|
|
* new clients from. The socket must be bound to a local
|
|
|
|
|
* address and listened to.
|
|
|
|
|
*
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
* For parallel calls to [class@Gio.SocketListener] methods to work, the socket
|
|
|
|
|
* must be in non-blocking mode. (See [property@Gio.Socket:blocking].)
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @source_object will be passed out in the various calls
|
|
|
|
|
* to accept to identify this particular source, which is
|
|
|
|
|
* useful if you're listening on multiple addresses and do
|
|
|
|
|
* different things depending on what address is connected to.
|
|
|
|
|
*
|
2014-06-23 15:01:28 +01:00
|
|
|
|
* The @socket will not be automatically closed when the @listener is finalized
|
|
|
|
|
* unless the listener held the final reference to the socket. Before GLib 2.42,
|
|
|
|
|
* the @socket was automatically closed on finalization of the @listener, even
|
|
|
|
|
* if references to it were held elsewhere.
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* Returns: %TRUE on success, %FALSE on error.
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
gboolean
|
2009-05-27 18:20:08 -04:00
|
|
|
|
g_socket_listener_add_socket (GSocketListener *listener,
|
|
|
|
|
GSocket *socket,
|
|
|
|
|
GObject *source_object,
|
|
|
|
|
GError **error)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
|
|
|
|
if (!check_listener (listener, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* TODO: Check that socket it is bound & not closed? */
|
|
|
|
|
|
|
|
|
|
if (g_socket_is_closed (socket))
|
|
|
|
|
{
|
|
|
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
|
_("Added socket is closed"));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
add_socket (listener, socket, source_object, FALSE);
|
2009-06-15 15:43:39 +02:00
|
|
|
|
|
|
|
|
|
if (G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
|
|
|
|
|
G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
|
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2009-05-18 13:02:11 +02:00
|
|
|
|
* g_socket_listener_add_address:
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @listener: a #GSocketListener
|
2009-05-20 12:01:29 +02:00
|
|
|
|
* @address: a #GSocketAddress
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @type: a #GSocketType
|
2009-05-20 12:01:29 +02:00
|
|
|
|
* @protocol: a #GSocketProtocol
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @source_object: (nullable): Optional #GObject identifying this source
|
|
|
|
|
* @effective_address: (out) (optional): location to store the address that was bound to, or %NULL.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @error: #GError for error reporting, or %NULL to ignore.
|
|
|
|
|
*
|
|
|
|
|
* Creates a socket of type @type and protocol @protocol, binds
|
|
|
|
|
* it to @address and adds it to the set of sockets we're accepting
|
|
|
|
|
* sockets from.
|
|
|
|
|
*
|
2009-06-12 13:01:04 -04:00
|
|
|
|
* Note that adding an IPv6 address, depending on the platform,
|
|
|
|
|
* may or may not result in a listener that also accepts IPv4
|
2011-04-20 19:08:06 +02:00
|
|
|
|
* connections. For more deterministic behavior, see
|
2009-06-12 13:01:04 -04:00
|
|
|
|
* g_socket_listener_add_inet_port().
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @source_object will be passed out in the various calls
|
|
|
|
|
* to accept to identify this particular source, which is
|
|
|
|
|
* useful if you're listening on multiple addresses and do
|
|
|
|
|
* different things depending on what address is connected to.
|
|
|
|
|
*
|
2009-06-15 14:23:57 +02:00
|
|
|
|
* If successful and @effective_address is non-%NULL then it will
|
2011-04-20 19:08:06 +02:00
|
|
|
|
* be set to the address that the binding actually occurred at. This
|
2009-06-15 14:23:57 +02:00
|
|
|
|
* is helpful for determining the port number that was used for when
|
|
|
|
|
* requesting a binding to port 0 (ie: "any port"). This address, if
|
|
|
|
|
* requested, belongs to the caller and must be freed.
|
|
|
|
|
*
|
2018-03-13 14:03:24 +00:00
|
|
|
|
* Call g_socket_listener_close() to stop listening on @address; this will not
|
|
|
|
|
* be done automatically when you drop your final reference to @listener, as
|
|
|
|
|
* references may be held internally.
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* Returns: %TRUE on success, %FALSE on error.
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
gboolean
|
2009-05-27 18:20:08 -04:00
|
|
|
|
g_socket_listener_add_address (GSocketListener *listener,
|
|
|
|
|
GSocketAddress *address,
|
|
|
|
|
GSocketType type,
|
|
|
|
|
GSocketProtocol protocol,
|
|
|
|
|
GObject *source_object,
|
2009-06-15 14:23:57 +02:00
|
|
|
|
GSocketAddress **effective_address,
|
2009-05-27 18:20:08 -04:00
|
|
|
|
GError **error)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2009-06-15 14:23:57 +02:00
|
|
|
|
GSocketAddress *local_address;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GSocketFamily family;
|
|
|
|
|
GSocket *socket;
|
|
|
|
|
|
|
|
|
|
if (!check_listener (listener, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
family = g_socket_address_get_family (address);
|
2009-05-20 12:01:29 +02:00
|
|
|
|
socket = g_socket_new (family, type, protocol, error);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
if (socket == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2009-05-19 12:06:29 +02:00
|
|
|
|
g_socket_set_listen_backlog (socket, listener->priv->listen_backlog);
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
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))
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
|
|
|
|
g_object_unref (socket);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENED, socket);
|
|
|
|
|
|
2009-06-15 15:42:38 +02:00
|
|
|
|
local_address = NULL;
|
2009-06-15 14:23:57 +02:00
|
|
|
|
if (effective_address)
|
|
|
|
|
{
|
|
|
|
|
local_address = g_socket_get_local_address (socket, error);
|
|
|
|
|
if (local_address == NULL)
|
|
|
|
|
{
|
|
|
|
|
g_object_unref (socket);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-15 15:42:38 +02:00
|
|
|
|
if (!g_socket_listener_add_socket (listener, socket,
|
|
|
|
|
source_object,
|
|
|
|
|
error))
|
|
|
|
|
{
|
|
|
|
|
if (local_address)
|
|
|
|
|
g_object_unref (local_address);
|
|
|
|
|
g_object_unref (socket);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (effective_address)
|
|
|
|
|
*effective_address = local_address;
|
|
|
|
|
|
|
|
|
|
g_object_unref (socket); /* add_socket refs this */
|
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_add_inet_port:
|
|
|
|
|
* @listener: a #GSocketListener
|
2009-06-12 13:01:04 -04:00
|
|
|
|
* @port: an IP port number (non-zero)
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @source_object: (nullable): Optional #GObject identifying this source
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @error: #GError for error reporting, or %NULL to ignore.
|
|
|
|
|
*
|
|
|
|
|
* Helper function for g_socket_listener_add_address() that
|
|
|
|
|
* creates a TCP/IP socket listening on IPv4 and IPv6 (if
|
|
|
|
|
* supported) on the specified port on all interfaces.
|
|
|
|
|
*
|
2019-02-19 18:08:59 +00:00
|
|
|
|
* If possible, the [class@Gio.SocketListener] will listen on both IPv4 and
|
|
|
|
|
* IPv6 (listening on the same port on both). If listening on one of the socket
|
|
|
|
|
* families fails, the [class@Gio.SocketListener] will only listen on the other.
|
|
|
|
|
* If listening on both fails, an error will be returned.
|
|
|
|
|
*
|
|
|
|
|
* If you need to distinguish whether listening on IPv4 or IPv6 or both was
|
|
|
|
|
* successful, connect to [signal@Gio.SocketListener::event].
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @source_object will be passed out in the various calls
|
|
|
|
|
* to accept to identify this particular source, which is
|
|
|
|
|
* useful if you're listening on multiple addresses and do
|
|
|
|
|
* different things depending on what address is connected to.
|
|
|
|
|
*
|
2018-03-13 14:03:24 +00:00
|
|
|
|
* Call g_socket_listener_close() to stop listening on @port; this will not
|
|
|
|
|
* be done automatically when you drop your final reference to @listener, as
|
|
|
|
|
* references may be held internally.
|
|
|
|
|
*
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* Returns: %TRUE on success, %FALSE on error.
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
gboolean
|
2009-05-27 18:20:08 -04:00
|
|
|
|
g_socket_listener_add_inet_port (GSocketListener *listener,
|
2009-06-12 15:57:51 +02:00
|
|
|
|
guint16 port,
|
2009-05-27 18:20:08 -04:00
|
|
|
|
GObject *source_object,
|
|
|
|
|
GError **error)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2009-06-12 13:01:04 -04:00
|
|
|
|
gboolean need_ipv4_socket = TRUE;
|
|
|
|
|
GSocket *socket4 = NULL;
|
|
|
|
|
GSocket *socket6;
|
2017-03-21 17:31:53 +01:00
|
|
|
|
GError *socket4_create_error = NULL;
|
|
|
|
|
GError *socket4_listen_error = NULL, *socket6_listen_error = NULL;
|
2009-06-12 13:01:04 -04:00
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (listener != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (port != 0, FALSE);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
if (!check_listener (listener, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2009-06-12 13:01:04 -04:00
|
|
|
|
/* first try to create an IPv6 socket */
|
|
|
|
|
socket6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
|
|
|
|
|
G_SOCKET_TYPE_STREAM,
|
|
|
|
|
G_SOCKET_PROTOCOL_DEFAULT,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
if (socket6 != NULL)
|
|
|
|
|
/* IPv6 is supported on this platform, so if we fail now it is
|
|
|
|
|
* a result of being unable to bind to our port. Don't fail
|
|
|
|
|
* silently as a result of this!
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
GInetAddress *inet_address;
|
|
|
|
|
GSocketAddress *address;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
2009-06-12 13:01:04 -04:00
|
|
|
|
inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
|
|
|
|
|
address = g_inet_socket_address_new (inet_address, port);
|
|
|
|
|
g_object_unref (inet_address);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
2009-06-12 13:01:04 -04:00
|
|
|
|
g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog);
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2009-06-12 13:01:04 -04:00
|
|
|
|
|
|
|
|
|
g_object_unref (address);
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BOUND, socket6);
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENING, socket6);
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
if (g_socket_listen (socket6, &socket6_listen_error))
|
2009-06-12 13:01:04 -04:00
|
|
|
|
{
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENED, socket6);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
/* If this socket already speaks IPv4 then we are done. */
|
|
|
|
|
if (g_socket_speaks_ipv4 (socket6))
|
|
|
|
|
need_ipv4_socket = FALSE;
|
|
|
|
|
}
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
2009-06-12 13:01:04 -04:00
|
|
|
|
|
|
|
|
|
if (need_ipv4_socket)
|
|
|
|
|
/* We are here for exactly one of the following reasons:
|
|
|
|
|
*
|
|
|
|
|
* - our platform doesn't support IPv6
|
|
|
|
|
* - we successfully created an IPv6 socket but it's V6ONLY
|
|
|
|
|
*
|
|
|
|
|
* In either case, we need to go ahead and create an IPv4 socket
|
|
|
|
|
* and fail the call if we can't bind to it.
|
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2009-06-12 13:01:04 -04:00
|
|
|
|
socket4 = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
|
|
|
|
G_SOCKET_TYPE_STREAM,
|
|
|
|
|
G_SOCKET_PROTOCOL_DEFAULT,
|
2017-03-21 17:31:53 +01:00
|
|
|
|
&socket4_create_error);
|
2009-06-12 13:01:04 -04:00
|
|
|
|
|
|
|
|
|
if (socket4 != NULL)
|
|
|
|
|
/* IPv4 is supported on this platform, so if we fail now it is
|
|
|
|
|
* a result of being unable to bind to our port. Don't fail
|
|
|
|
|
* silently as a result of this!
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
GInetAddress *inet_address;
|
|
|
|
|
GSocketAddress *address;
|
|
|
|
|
|
|
|
|
|
inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
|
|
|
|
|
address = g_inet_socket_address_new (inet_address, port);
|
|
|
|
|
g_object_unref (inet_address);
|
|
|
|
|
|
|
|
|
|
g_socket_set_listen_backlog (socket4,
|
|
|
|
|
listener->priv->listen_backlog);
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BINDING, socket4);
|
|
|
|
|
|
|
|
|
|
if (!g_socket_bind (socket4, address, TRUE, error))
|
|
|
|
|
{
|
2025-02-07 14:45:27 +00:00
|
|
|
|
g_clear_object (&address);
|
|
|
|
|
g_clear_object (&socket4);
|
|
|
|
|
g_clear_object (&socket6);
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_clear_error (&socket6_listen_error);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2009-06-12 13:01:04 -04:00
|
|
|
|
|
|
|
|
|
g_object_unref (address);
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BOUND, socket4);
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENING, socket4);
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
if (g_socket_listen (socket4, &socket4_listen_error))
|
2009-06-12 13:01:04 -04:00
|
|
|
|
{
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENED, socket4);
|
2009-06-12 13:01:04 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Ok. So IPv4 is not supported on this platform. If we
|
|
|
|
|
* succeeded at creating an IPv6 socket then that's OK, but
|
|
|
|
|
* otherwise we need to tell the user we failed.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2017-03-21 17:31:53 +01:00
|
|
|
|
if (socket6 == NULL || socket6_listen_error != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_clear_object (&socket6);
|
|
|
|
|
g_clear_error (&socket6_listen_error);
|
|
|
|
|
g_propagate_error (error, g_steal_pointer (&socket4_create_error));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2009-06-12 13:01:04 -04:00
|
|
|
|
}
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
/* Only error out if both listen() calls failed. */
|
|
|
|
|
if ((socket6 == NULL || socket6_listen_error != NULL) &&
|
|
|
|
|
(socket4 == NULL || socket4_listen_error != NULL))
|
|
|
|
|
{
|
|
|
|
|
GError **set_error = ((socket6_listen_error != NULL) ?
|
|
|
|
|
&socket6_listen_error :
|
|
|
|
|
&socket4_listen_error);
|
|
|
|
|
g_propagate_error (error, g_steal_pointer (set_error));
|
|
|
|
|
|
|
|
|
|
g_clear_error (&socket6_listen_error);
|
|
|
|
|
g_clear_error (&socket4_listen_error);
|
|
|
|
|
|
|
|
|
|
g_clear_object (&socket6);
|
|
|
|
|
g_clear_object (&socket4);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-12 13:01:04 -04:00
|
|
|
|
g_assert (socket6 != NULL || socket4 != NULL);
|
|
|
|
|
|
|
|
|
|
if (socket6 != NULL)
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
add_socket (listener, socket6, source_object, TRUE);
|
2009-06-12 13:01:04 -04:00
|
|
|
|
|
|
|
|
|
if (socket4 != NULL)
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
add_socket (listener, socket4, source_object, TRUE);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
2009-06-12 13:01:04 -04:00
|
|
|
|
if (G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
|
|
|
|
|
G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_clear_error (&socket6_listen_error);
|
|
|
|
|
g_clear_error (&socket4_listen_error);
|
|
|
|
|
|
2025-07-25 15:31:53 +01:00
|
|
|
|
g_clear_object (&socket6);
|
|
|
|
|
g_clear_object (&socket4);
|
|
|
|
|
|
2009-06-12 13:01:04 -04:00
|
|
|
|
return TRUE;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GList *
|
2009-05-27 18:20:08 -04:00
|
|
|
|
add_sources (GSocketListener *listener,
|
|
|
|
|
GSocketSourceFunc callback,
|
|
|
|
|
gpointer callback_data,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GMainContext *context)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
|
|
|
|
GSocket *socket;
|
|
|
|
|
GSource *source;
|
|
|
|
|
GList *sources;
|
2020-11-17 11:50:59 +01:00
|
|
|
|
guint i;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
sources = NULL;
|
|
|
|
|
for (i = 0; i < listener->priv->sockets->len; i++)
|
|
|
|
|
{
|
|
|
|
|
socket = listener->priv->sockets->pdata[i];
|
|
|
|
|
|
|
|
|
|
source = g_socket_create_source (socket, G_IO_IN, cancellable);
|
|
|
|
|
g_source_set_callback (source,
|
|
|
|
|
(GSourceFunc) callback,
|
|
|
|
|
callback_data, NULL);
|
|
|
|
|
g_source_attach (source, context);
|
|
|
|
|
|
|
|
|
|
sources = g_list_prepend (sources, source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
free_sources (GList *sources)
|
|
|
|
|
{
|
|
|
|
|
GSource *source;
|
|
|
|
|
while (sources != NULL)
|
|
|
|
|
{
|
|
|
|
|
source = sources->data;
|
|
|
|
|
sources = g_list_delete_link (sources, sources);
|
|
|
|
|
g_source_destroy (source);
|
|
|
|
|
g_source_unref (source);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct AcceptData {
|
|
|
|
|
GMainLoop *loop;
|
|
|
|
|
GSocket *socket;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2009-05-27 18:20:08 -04:00
|
|
|
|
accept_callback (GSocket *socket,
|
|
|
|
|
GIOCondition condition,
|
|
|
|
|
gpointer user_data)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
|
|
|
|
struct AcceptData *data = user_data;
|
|
|
|
|
|
|
|
|
|
data->socket = socket;
|
|
|
|
|
g_main_loop_quit (data->loop);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_accept_socket:
|
|
|
|
|
* @listener: a #GSocketListener
|
2017-04-28 12:29:44 +01:00
|
|
|
|
* @source_object: (out) (transfer none) (optional) (nullable): location where #GObject pointer will be stored, or %NULL.
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @error: #GError for error reporting, or %NULL to ignore.
|
|
|
|
|
*
|
|
|
|
|
* Blocks waiting for a client to connect to any of the sockets added
|
|
|
|
|
* to the listener. Returns the #GSocket that was accepted.
|
|
|
|
|
*
|
|
|
|
|
* If you want to accept the high-level #GSocketConnection, not a #GSocket,
|
|
|
|
|
* which is often the case, then you should use g_socket_listener_accept()
|
|
|
|
|
* instead.
|
|
|
|
|
*
|
|
|
|
|
* If @source_object is not %NULL it will be filled out with the source
|
|
|
|
|
* object specified when the corresponding socket or address was added
|
|
|
|
|
* to the listener.
|
|
|
|
|
*
|
2009-05-27 18:20:08 -04:00
|
|
|
|
* If @cancellable is not %NULL, then the operation can be cancelled by
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* triggering the cancellable object from another thread. If the operation
|
2009-05-27 18:20:08 -04:00
|
|
|
|
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
2010-09-24 18:24:41 -03:00
|
|
|
|
* Returns: (transfer full): a #GSocket on success, %NULL on error.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GSocket *
|
|
|
|
|
g_socket_listener_accept_socket (GSocketListener *listener,
|
|
|
|
|
GObject **source_object,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GSocket *accept_socket, *socket;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), NULL);
|
|
|
|
|
|
|
|
|
|
if (!check_listener (listener, error))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (listener->priv->sockets->len == 1)
|
|
|
|
|
{
|
|
|
|
|
accept_socket = listener->priv->sockets->pdata[0];
|
|
|
|
|
if (!g_socket_condition_wait (accept_socket, G_IO_IN,
|
|
|
|
|
cancellable, error))
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GList *sources;
|
|
|
|
|
struct AcceptData data;
|
|
|
|
|
GMainLoop *loop;
|
|
|
|
|
|
|
|
|
|
if (listener->priv->main_context == NULL)
|
|
|
|
|
listener->priv->main_context = g_main_context_new ();
|
|
|
|
|
|
|
|
|
|
loop = g_main_loop_new (listener->priv->main_context, FALSE);
|
|
|
|
|
data.loop = loop;
|
|
|
|
|
sources = add_sources (listener,
|
|
|
|
|
accept_callback,
|
|
|
|
|
&data,
|
|
|
|
|
cancellable,
|
|
|
|
|
listener->priv->main_context);
|
|
|
|
|
g_main_loop_run (loop);
|
|
|
|
|
accept_socket = data.socket;
|
|
|
|
|
free_sources (sources);
|
|
|
|
|
g_main_loop_unref (loop);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-23 17:42:01 -04:00
|
|
|
|
if (!(socket = g_socket_accept (accept_socket, cancellable, error)))
|
2009-05-15 21:26:24 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (source_object)
|
|
|
|
|
*source_object = g_object_get_qdata (G_OBJECT (accept_socket), source_quark);
|
|
|
|
|
|
|
|
|
|
return socket;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_accept:
|
|
|
|
|
* @listener: a #GSocketListener
|
2017-04-28 12:29:44 +01:00
|
|
|
|
* @source_object: (out) (transfer none) (optional) (nullable): location where #GObject pointer will be stored, or %NULL
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @error: #GError for error reporting, or %NULL to ignore.
|
|
|
|
|
*
|
|
|
|
|
* Blocks waiting for a client to connect to any of the sockets added
|
|
|
|
|
* to the listener. Returns a #GSocketConnection for the socket that was
|
|
|
|
|
* accepted.
|
|
|
|
|
*
|
|
|
|
|
* If @source_object is not %NULL it will be filled out with the source
|
|
|
|
|
* object specified when the corresponding socket or address was added
|
|
|
|
|
* to the listener.
|
|
|
|
|
*
|
2009-05-27 18:20:08 -04:00
|
|
|
|
* If @cancellable is not %NULL, then the operation can be cancelled by
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* triggering the cancellable object from another thread. If the operation
|
2009-05-27 18:20:08 -04:00
|
|
|
|
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
2010-09-24 18:24:41 -03:00
|
|
|
|
* Returns: (transfer full): a #GSocketConnection on success, %NULL on error.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GSocketConnection *
|
|
|
|
|
g_socket_listener_accept (GSocketListener *listener,
|
|
|
|
|
GObject **source_object,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GSocketConnection *connection;
|
|
|
|
|
GSocket *socket;
|
|
|
|
|
|
|
|
|
|
socket = g_socket_listener_accept_socket (listener,
|
|
|
|
|
source_object,
|
|
|
|
|
cancellable,
|
|
|
|
|
error);
|
|
|
|
|
if (socket == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
connection = g_socket_connection_factory_create_connection (socket);
|
|
|
|
|
g_object_unref (socket);
|
|
|
|
|
|
|
|
|
|
return connection;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-08 00:05:52 +00:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GList *sources; /* (element-type GSource) */
|
|
|
|
|
} AcceptSocketAsyncData;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
accept_socket_async_data_free (AcceptSocketAsyncData *data)
|
|
|
|
|
{
|
|
|
|
|
free_sources (data->sources);
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
static gboolean
|
2009-05-27 18:20:08 -04:00
|
|
|
|
accept_ready (GSocket *accept_socket,
|
|
|
|
|
GIOCondition condition,
|
2012-08-02 15:48:22 -04:00
|
|
|
|
gpointer user_data)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2012-08-02 15:48:22 -04:00
|
|
|
|
GTask *task = user_data;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GError *error = NULL;
|
2009-06-23 17:42:01 -04:00
|
|
|
|
GSocket *socket;
|
|
|
|
|
GObject *source_object;
|
2019-02-08 00:05:52 +00:00
|
|
|
|
|
|
|
|
|
/* Don’t call g_task_return_*() multiple times if we have multiple incoming
|
2025-07-25 15:40:50 +01:00
|
|
|
|
* connections in the same `GMainContext` iteration. We expect `GMainContext`
|
|
|
|
|
* to guarantee this behaviour, but let’s double check that the other sources
|
|
|
|
|
* have been destroyed correctly. */
|
|
|
|
|
g_assert (!g_source_is_destroyed (g_main_current_source ()));
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
2012-08-02 15:48:22 -04:00
|
|
|
|
socket = g_socket_accept (accept_socket, g_task_get_cancellable (task), &error);
|
2009-06-23 17:42:01 -04:00
|
|
|
|
if (socket)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2009-06-23 17:42:01 -04:00
|
|
|
|
source_object = g_object_get_qdata (G_OBJECT (accept_socket), source_quark);
|
|
|
|
|
if (source_object)
|
2012-08-02 15:48:22 -04:00
|
|
|
|
g_object_set_qdata_full (G_OBJECT (task),
|
2009-06-23 17:42:01 -04:00
|
|
|
|
source_quark,
|
|
|
|
|
g_object_ref (source_object), g_object_unref);
|
2012-12-03 15:59:06 +01:00
|
|
|
|
g_task_return_pointer (task, socket, g_object_unref);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
2009-06-23 17:42:01 -04:00
|
|
|
|
else
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
/* This can happen if there are multiple pending g_socket_listener_accept_async()
|
|
|
|
|
* calls (say N), and fewer queued incoming connections (say C) than that
|
|
|
|
|
* on this `GSocket`, in a single `GMainContext` iteration.
|
|
|
|
|
* (There may still be queued incoming connections on other `GSocket`s in
|
|
|
|
|
* the `GSocketListener`.)
|
|
|
|
|
*
|
|
|
|
|
* If so, the `GSocketSource`s for that pending g_socket_listener_accept_async()
|
|
|
|
|
* call will all raise `G_IO_IN` in this `GMainContext` iteration. The
|
|
|
|
|
* first C calls to accept_ready() succeed, but the following N-C calls
|
|
|
|
|
* would block, as all the queued incoming connections on this `GSocket`
|
|
|
|
|
* have been accepted by then. The `GSocketSource`s for these remaining
|
|
|
|
|
* g_socket_listener_accept_async() calls will not have been destroyed,
|
|
|
|
|
* because there’s an independent set of `GSocketSource`s for each
|
|
|
|
|
* g_socket_listener_accept_async() call, rather than one `GSocketSource`
|
|
|
|
|
* for each socket in the `GSocketListener`.
|
|
|
|
|
*
|
|
|
|
|
* This is why we need sockets in the `GSocketListener` to be non-blocking:
|
|
|
|
|
* otherwise the above g_socket_accept() call would block. */
|
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
|
|
|
|
{
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-02 15:48:22 -04:00
|
|
|
|
g_task_return_error (task, error);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 15:40:50 +01:00
|
|
|
|
/* Explicitly clear the task data so we know the other sources are destroyed. */
|
|
|
|
|
g_task_set_task_data (task, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
/* Drop the final reference to the @task. */
|
2012-08-02 15:48:22 -04:00
|
|
|
|
g_object_unref (task);
|
2019-02-08 00:05:52 +00:00
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_accept_socket_async:
|
|
|
|
|
* @listener: a #GSocketListener
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @cancellable: (nullable): a #GCancellable, or %NULL
|
2010-12-29 16:01:12 +01:00
|
|
|
|
* @callback: (scope async): a #GAsyncReadyCallback
|
2023-02-20 14:43:02 -06:00
|
|
|
|
* @user_data: user data for the callback
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* This is the asynchronous version of g_socket_listener_accept_socket().
|
|
|
|
|
*
|
|
|
|
|
* When the operation is finished @callback will be
|
2009-05-27 18:20:08 -04:00
|
|
|
|
* called. You can then call g_socket_listener_accept_socket_finish()
|
|
|
|
|
* to get the result of the operation.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
void
|
2009-05-27 18:20:08 -04:00
|
|
|
|
g_socket_listener_accept_socket_async (GSocketListener *listener,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GAsyncReadyCallback callback,
|
|
|
|
|
gpointer user_data)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2012-08-02 15:48:22 -04:00
|
|
|
|
GTask *task;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GError *error = NULL;
|
2019-02-08 00:05:52 +00:00
|
|
|
|
AcceptSocketAsyncData *data = NULL;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
2012-08-02 15:48:22 -04:00
|
|
|
|
task = g_task_new (listener, cancellable, callback, user_data);
|
2016-06-16 19:39:38 -04:00
|
|
|
|
g_task_set_source_tag (task, g_socket_listener_accept_socket_async);
|
2012-08-02 15:48:22 -04:00
|
|
|
|
|
2009-05-15 21:26:24 +02:00
|
|
|
|
if (!check_listener (listener, &error))
|
|
|
|
|
{
|
2012-08-02 15:48:22 -04:00
|
|
|
|
g_task_return_error (task, error);
|
|
|
|
|
g_object_unref (task);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 15:40:50 +01:00
|
|
|
|
/* This transfers the one strong ref of @task to all of the sources. The first
|
|
|
|
|
* source to be ready will call g_socket_accept() and take ownership of the
|
|
|
|
|
* @task. The other callbacks have to return early (if dispatched) after
|
|
|
|
|
* that. */
|
2019-02-08 00:05:52 +00:00
|
|
|
|
data = g_new0 (AcceptSocketAsyncData, 1);
|
|
|
|
|
data->sources = add_sources (listener,
|
2012-08-02 15:48:22 -04:00
|
|
|
|
accept_ready,
|
|
|
|
|
task,
|
|
|
|
|
cancellable,
|
|
|
|
|
g_main_context_get_thread_default ());
|
2019-02-08 00:05:52 +00:00
|
|
|
|
g_task_set_task_data (task, g_steal_pointer (&data),
|
|
|
|
|
(GDestroyNotify) accept_socket_async_data_free);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_accept_socket_finish:
|
|
|
|
|
* @listener: a #GSocketListener
|
|
|
|
|
* @result: a #GAsyncResult.
|
2017-04-28 12:29:44 +01:00
|
|
|
|
* @source_object: (out) (transfer none) (optional) (nullable): Optional #GObject identifying this source
|
2011-04-20 19:08:06 +02:00
|
|
|
|
* @error: a #GError location to store the error occurring, or %NULL to
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* ignore.
|
|
|
|
|
*
|
|
|
|
|
* Finishes an async accept operation. See g_socket_listener_accept_socket_async()
|
|
|
|
|
*
|
2010-09-24 18:24:41 -03:00
|
|
|
|
* Returns: (transfer full): a #GSocket on success, %NULL on error.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GSocket *
|
2009-05-27 18:20:08 -04:00
|
|
|
|
g_socket_listener_accept_socket_finish (GSocketListener *listener,
|
|
|
|
|
GAsyncResult *result,
|
|
|
|
|
GObject **source_object,
|
|
|
|
|
GError **error)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
2010-07-10 19:16:27 -04:00
|
|
|
|
g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), NULL);
|
2012-08-02 15:48:22 -04:00
|
|
|
|
g_return_val_if_fail (g_task_is_valid (result, listener), NULL);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
if (source_object)
|
|
|
|
|
*source_object = g_object_get_qdata (G_OBJECT (result), source_quark);
|
|
|
|
|
|
2012-08-02 15:48:22 -04:00
|
|
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
2009-05-15 21:26:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2009-05-18 13:02:11 +02:00
|
|
|
|
* g_socket_listener_accept_async:
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @listener: a #GSocketListener
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @cancellable: (nullable): a #GCancellable, or %NULL
|
2010-12-29 16:01:12 +01:00
|
|
|
|
* @callback: (scope async): a #GAsyncReadyCallback
|
2023-02-20 14:43:02 -06:00
|
|
|
|
* @user_data: user data for the callback
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* This is the asynchronous version of g_socket_listener_accept().
|
|
|
|
|
*
|
|
|
|
|
* When the operation is finished @callback will be
|
2021-04-16 23:08:09 +09:00
|
|
|
|
* called. You can then call g_socket_listener_accept_finish()
|
2009-05-27 18:20:08 -04:00
|
|
|
|
* to get the result of the operation.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
void
|
|
|
|
|
g_socket_listener_accept_async (GSocketListener *listener,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GAsyncReadyCallback callback,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
g_socket_listener_accept_socket_async (listener,
|
|
|
|
|
cancellable,
|
|
|
|
|
callback,
|
|
|
|
|
user_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_accept_finish:
|
|
|
|
|
* @listener: a #GSocketListener
|
|
|
|
|
* @result: a #GAsyncResult.
|
2017-04-28 12:29:44 +01:00
|
|
|
|
* @source_object: (out) (transfer none) (optional) (nullable): Optional #GObject identifying this source
|
2011-04-20 19:08:06 +02:00
|
|
|
|
* @error: a #GError location to store the error occurring, or %NULL to
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* ignore.
|
|
|
|
|
*
|
|
|
|
|
* Finishes an async accept operation. See g_socket_listener_accept_async()
|
|
|
|
|
*
|
2010-09-24 18:24:41 -03:00
|
|
|
|
* Returns: (transfer full): a #GSocketConnection on success, %NULL on error.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
GSocketConnection *
|
2009-05-27 18:20:08 -04:00
|
|
|
|
g_socket_listener_accept_finish (GSocketListener *listener,
|
|
|
|
|
GAsyncResult *result,
|
|
|
|
|
GObject **source_object,
|
|
|
|
|
GError **error)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
|
|
|
|
GSocket *socket;
|
|
|
|
|
GSocketConnection *connection;
|
|
|
|
|
|
|
|
|
|
socket = g_socket_listener_accept_socket_finish (listener,
|
|
|
|
|
result,
|
|
|
|
|
source_object,
|
|
|
|
|
error);
|
|
|
|
|
if (socket == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
connection = g_socket_connection_factory_create_connection (socket);
|
|
|
|
|
g_object_unref (socket);
|
|
|
|
|
return connection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2009-05-18 13:02:11 +02:00
|
|
|
|
* g_socket_listener_set_backlog:
|
2009-05-15 21:26:24 +02:00
|
|
|
|
* @listener: a #GSocketListener
|
|
|
|
|
* @listen_backlog: an integer
|
|
|
|
|
*
|
2019-06-17 15:19:46 +01:00
|
|
|
|
* Sets the listen backlog on the sockets in the listener. This must be called
|
|
|
|
|
* before adding any sockets, addresses or ports to the #GSocketListener (for
|
|
|
|
|
* example, by calling g_socket_listener_add_inet_port()) to be effective.
|
2009-05-15 21:26:24 +02:00
|
|
|
|
*
|
|
|
|
|
* See g_socket_set_listen_backlog() for details
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
void
|
|
|
|
|
g_socket_listener_set_backlog (GSocketListener *listener,
|
2009-05-27 18:20:08 -04:00
|
|
|
|
int listen_backlog)
|
2009-05-15 21:26:24 +02:00
|
|
|
|
{
|
|
|
|
|
GSocket *socket;
|
2020-11-17 11:51:48 +01:00
|
|
|
|
guint i;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
if (listener->priv->closed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
listener->priv->listen_backlog = listen_backlog;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < listener->priv->sockets->len; i++)
|
|
|
|
|
{
|
|
|
|
|
socket = listener->priv->sockets->pdata[i];
|
|
|
|
|
g_socket_set_listen_backlog (socket, listen_backlog);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_close:
|
|
|
|
|
* @listener: a #GSocketListener
|
|
|
|
|
*
|
|
|
|
|
* Closes all the sockets in the listener.
|
|
|
|
|
*
|
|
|
|
|
* Since: 2.22
|
2009-05-27 18:20:08 -04:00
|
|
|
|
*/
|
2009-05-15 21:26:24 +02:00
|
|
|
|
void
|
|
|
|
|
g_socket_listener_close (GSocketListener *listener)
|
|
|
|
|
{
|
|
|
|
|
GSocket *socket;
|
2020-11-17 11:52:33 +01:00
|
|
|
|
guint i;
|
2009-05-15 21:26:24 +02:00
|
|
|
|
|
|
|
|
|
g_return_if_fail (G_IS_SOCKET_LISTENER (listener));
|
|
|
|
|
|
|
|
|
|
if (listener->priv->closed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < listener->priv->sockets->len; i++)
|
|
|
|
|
{
|
|
|
|
|
socket = listener->priv->sockets->pdata[i];
|
|
|
|
|
g_socket_close (socket, NULL);
|
|
|
|
|
}
|
|
|
|
|
listener->priv->closed = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
/**
|
|
|
|
|
* g_socket_listener_add_any_inet_port:
|
|
|
|
|
* @listener: a #GSocketListener
|
2016-10-28 18:29:02 -07:00
|
|
|
|
* @source_object: (nullable): Optional #GObject identifying this source
|
2011-04-20 19:08:06 +02:00
|
|
|
|
* @error: a #GError location to store the error occurring, or %NULL to
|
2009-06-15 14:07:13 -04:00
|
|
|
|
* ignore.
|
|
|
|
|
*
|
|
|
|
|
* Listens for TCP connections on any available port number for both
|
2011-04-20 19:08:06 +02:00
|
|
|
|
* IPv6 and IPv4 (if each is available).
|
2009-06-15 14:07:13 -04:00
|
|
|
|
*
|
|
|
|
|
* This is useful if you need to have a socket for incoming connections
|
|
|
|
|
* but don't care about the specific port number.
|
|
|
|
|
*
|
2019-02-11 00:32:47 +00:00
|
|
|
|
* If possible, the [class@Gio.SocketListener] will listen on both IPv4 and
|
|
|
|
|
* IPv6 (listening on the same port on both). If listening on one of the socket
|
|
|
|
|
* families fails, the [class@Gio.SocketListener] will only listen on the other.
|
|
|
|
|
* If listening on both fails, an error will be returned.
|
|
|
|
|
*
|
|
|
|
|
* If you need to distinguish whether listening on IPv4 or IPv6 or both was
|
|
|
|
|
* successful, connect to [signal@Gio.SocketListener::event].
|
|
|
|
|
*
|
2009-06-15 14:07:13 -04:00
|
|
|
|
* @source_object will be passed out in the various calls
|
|
|
|
|
* to accept to identify this particular source, which is
|
|
|
|
|
* useful if you're listening on multiple addresses and do
|
|
|
|
|
* different things depending on what address is connected to.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the port number, or 0 in case of failure.
|
|
|
|
|
*
|
2009-11-19 10:19:01 -06:00
|
|
|
|
* Since: 2.24
|
2009-06-15 14:07:13 -04:00
|
|
|
|
**/
|
|
|
|
|
guint16
|
|
|
|
|
g_socket_listener_add_any_inet_port (GSocketListener *listener,
|
|
|
|
|
GObject *source_object,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GSList *sockets_to_close = NULL;
|
|
|
|
|
guint16 candidate_port = 0;
|
|
|
|
|
GSocket *socket6 = NULL;
|
|
|
|
|
GSocket *socket4 = NULL;
|
|
|
|
|
gint attempts = 37;
|
2017-03-21 17:31:53 +01:00
|
|
|
|
GError *socket4_listen_error = NULL, *socket6_listen_error = NULL;
|
2009-06-15 14:07:13 -04:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* multi-step process:
|
|
|
|
|
* - first, create an IPv6 socket.
|
|
|
|
|
* - if that fails, create an IPv4 socket and bind it to port 0 and
|
|
|
|
|
* that's it. no retries if that fails (why would it?).
|
|
|
|
|
* - if our IPv6 socket also speaks IPv4 then we are done.
|
|
|
|
|
* - if not, then we need to create a IPv4 socket with the same port
|
|
|
|
|
* number. this might fail, of course. so we try this a bunch of
|
|
|
|
|
* times -- leaving the old IPv6 sockets open so that we get a
|
|
|
|
|
* different port number to try each time.
|
|
|
|
|
* - if all that fails then just give up.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
while (attempts--)
|
|
|
|
|
{
|
|
|
|
|
GInetAddress *inet_address;
|
|
|
|
|
GSocketAddress *address;
|
|
|
|
|
gboolean result;
|
|
|
|
|
|
|
|
|
|
g_assert (socket6 == NULL);
|
|
|
|
|
socket6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
|
|
|
|
|
G_SOCKET_TYPE_STREAM,
|
|
|
|
|
G_SOCKET_PROTOCOL_DEFAULT,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
if (socket6 != NULL)
|
|
|
|
|
{
|
|
|
|
|
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);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BINDING, socket6);
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
result = g_socket_bind (socket6, address, TRUE, error);
|
|
|
|
|
g_object_unref (address);
|
|
|
|
|
|
|
|
|
|
if (!result ||
|
|
|
|
|
!(address = g_socket_get_local_address (socket6, error)))
|
|
|
|
|
{
|
2025-02-07 14:45:27 +00:00
|
|
|
|
g_clear_object (&socket6);
|
2009-06-15 14:07:13 -04:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BOUND, socket6);
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
g_assert (G_IS_INET_SOCKET_ADDRESS (address));
|
|
|
|
|
candidate_port =
|
|
|
|
|
g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address));
|
|
|
|
|
g_assert (candidate_port != 0);
|
|
|
|
|
g_object_unref (address);
|
|
|
|
|
|
|
|
|
|
if (g_socket_speaks_ipv4 (socket6))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_assert (socket4 == NULL);
|
|
|
|
|
socket4 = g_socket_new (G_SOCKET_FAMILY_IPV4,
|
|
|
|
|
G_SOCKET_TYPE_STREAM,
|
|
|
|
|
G_SOCKET_PROTOCOL_DEFAULT,
|
|
|
|
|
socket6 ? NULL : error);
|
|
|
|
|
|
|
|
|
|
if (socket4 == NULL)
|
|
|
|
|
/* IPv4 not supported.
|
|
|
|
|
* if IPv6 is supported then candidate_port will be non-zero
|
|
|
|
|
* (and the error parameter above will have been NULL)
|
|
|
|
|
* if IPv6 is unsupported then candidate_port will be zero
|
|
|
|
|
* (and error will have been set by the above call)
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
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);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BINDING, socket4);
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
/* a note on the 'error' clause below:
|
|
|
|
|
*
|
|
|
|
|
* if candidate_port is 0 then we report the error right away
|
|
|
|
|
* since it is strange that this binding would fail at all.
|
|
|
|
|
* otherwise, we ignore the error message (ie: NULL).
|
|
|
|
|
*
|
|
|
|
|
* the exception to this rule is the last time through the loop
|
|
|
|
|
* (ie: attempts == 0) in which case we want to set the error
|
|
|
|
|
* because failure here means that the entire call will fail and
|
|
|
|
|
* we need something to show to the user.
|
|
|
|
|
*
|
|
|
|
|
* an english summary of the situation: "if we gave a candidate
|
|
|
|
|
* port number AND we have more attempts to try, then ignore the
|
|
|
|
|
* error for now".
|
|
|
|
|
*/
|
|
|
|
|
result = g_socket_bind (socket4, address, TRUE,
|
|
|
|
|
(candidate_port && attempts) ? NULL : error);
|
|
|
|
|
g_object_unref (address);
|
|
|
|
|
|
|
|
|
|
if (candidate_port)
|
|
|
|
|
{
|
|
|
|
|
g_assert (socket6 != NULL);
|
|
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
|
/* got our candidate port successfully */
|
2014-10-09 15:54:43 +02:00
|
|
|
|
{
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BOUND, socket4);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-06-15 14:07:13 -04:00
|
|
|
|
else
|
|
|
|
|
/* we failed to bind to the specified port. try again. */
|
|
|
|
|
{
|
2025-02-07 14:45:27 +00:00
|
|
|
|
g_clear_object (&socket4);
|
2009-06-15 14:07:13 -04:00
|
|
|
|
|
|
|
|
|
/* keep this open so we get a different port number */
|
|
|
|
|
sockets_to_close = g_slist_prepend (sockets_to_close,
|
2025-02-07 14:45:27 +00:00
|
|
|
|
g_steal_pointer (&socket6));
|
2009-06-15 14:07:13 -04:00
|
|
|
|
candidate_port = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* we didn't tell it a port. this means two things.
|
|
|
|
|
* - if we failed, then something really bad happened.
|
|
|
|
|
* - if we succeeded, then we need to find out the port number.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
g_assert (socket6 == NULL);
|
|
|
|
|
|
|
|
|
|
if (!result ||
|
|
|
|
|
!(address = g_socket_get_local_address (socket4, error)))
|
|
|
|
|
{
|
2025-02-07 14:45:27 +00:00
|
|
|
|
g_clear_object (&socket4);
|
2009-06-15 14:07:13 -04:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-09 15:54:43 +02:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_BOUND, socket4);
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
g_assert (G_IS_INET_SOCKET_ADDRESS (address));
|
|
|
|
|
candidate_port =
|
|
|
|
|
g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address));
|
|
|
|
|
g_assert (candidate_port != 0);
|
|
|
|
|
g_object_unref (address);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* should only be non-zero if we have a socket */
|
|
|
|
|
g_assert ((candidate_port != 0) == (socket4 || socket6));
|
|
|
|
|
|
2025-02-07 14:45:27 +00:00
|
|
|
|
g_slist_free_full (g_steal_pointer (&sockets_to_close), g_object_unref);
|
2009-06-15 14:07:13 -04:00
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
/* now we actually listen() the sockets and add them to the listener. If
|
|
|
|
|
* either of the listen()s fails, only return the other socket. Fail if both
|
|
|
|
|
* failed. */
|
2009-06-15 14:07:13 -04:00
|
|
|
|
if (socket6 != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_socket_set_listen_backlog (socket6, listener->priv->listen_backlog);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENING, socket6);
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
if (g_socket_listen (socket6, &socket6_listen_error))
|
2009-06-15 14:07:13 -04:00
|
|
|
|
{
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENED, socket6);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
add_socket (listener, socket6, source_object, TRUE);
|
2017-03-21 17:31:53 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_clear_object (&socket6);
|
|
|
|
|
}
|
2009-06-15 14:07:13 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (socket4 != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_socket_set_listen_backlog (socket4, listener->priv->listen_backlog);
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENING, socket4);
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
if (g_socket_listen (socket4, &socket4_listen_error))
|
2009-06-15 14:07:13 -04:00
|
|
|
|
{
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_signal_emit (listener, signals[EVENT], 0,
|
|
|
|
|
G_SOCKET_LISTENER_LISTENED, socket4);
|
|
|
|
|
|
gsocketlistener: Fix infinite blocking when accepting connections
As the new comments in the code try to explain, this fixes infinite
blocking which could happen when calling
`g_socket_listener_accept_async()` multiple times in parallel, with more
parallel calls than there are pending incoming connections on any of the
`GSocket`s in the `GSocketListener`.
The way `g_socket_listener_accept_async()` works is to create a set of
`GSocketSource`s when it’s called, one for each of the `GSocket`s in the
`GSocketListener`. Those sources are attached to the main context,
polling for `G_IO_IN` (indicating that the socket has a pending incoming
connection to accept).
When one of the socket sources polls ready, `g_socket_accept()` is
called on it, and a new connection is created.
If there are multiple pending `g_socket_listener_accept_async()` calls,
there are correspondingly multiple `GSocketSource` sources for each
`GSocket` in the `GSocketListener`. They will all poll ready in a single
`GMainContext` iteration. The first one to be dispatched will
successfully call `g_socket_accept()`, and subsequent ones to dispatch
will do likewise until there are no more pending incoming connections.
At that point, any remaining socket sources polling ready in that
`GMainContext` iteration will call `g_socket_accept()` on a socket which
is *not* ready to accept, and that will block indefinitely, because
`GSocket` has its own blocking layer on top of `poll()`.
This is not great.
It seems like a better approach would be to disable `GSocket`’s blocking
code, because `GSocketListener` is using `poll()` directly. We only need
one source of poll truth. So, do that.
Unfortunately, that’s complicated by the fact that
`g_socket_listener_add_socket()` allows third party code to provide its
own `GSocket`s to listen on. We probably can’t unilaterally change those
to non-blocking mode, so users of that API will get what they ask for.
That might include blocking indefinitely. I’ve adjusted the
documentation to mention that, at least.
The changes are fairly simple; the accompanying unit test is less
simple. Shrug. It tests for the scenario fixed by this commit, plus the
scenario fixed by the previous commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3739
2025-07-25 15:55:34 +01:00
|
|
|
|
add_socket (listener, socket4, source_object, TRUE);
|
2017-03-21 17:31:53 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_clear_object (&socket4);
|
2009-06-15 14:07:13 -04:00
|
|
|
|
}
|
2017-03-21 17:31:53 +01:00
|
|
|
|
}
|
2009-06-15 14:07:13 -04:00
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
/* Error out if both listen() calls failed (or if there’s no separate IPv4
|
|
|
|
|
* socket and the IPv6 listen() call failed). */
|
|
|
|
|
if (socket6_listen_error != NULL &&
|
|
|
|
|
(socket4 == NULL || socket4_listen_error != NULL))
|
|
|
|
|
{
|
|
|
|
|
GError **set_error = ((socket6_listen_error != NULL) ?
|
|
|
|
|
&socket6_listen_error :
|
|
|
|
|
&socket4_listen_error);
|
|
|
|
|
g_propagate_error (error, g_steal_pointer (set_error));
|
2014-10-09 15:54:43 +02:00
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_clear_error (&socket6_listen_error);
|
|
|
|
|
g_clear_error (&socket4_listen_error);
|
2009-06-15 14:07:13 -04:00
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_clear_object (&socket6);
|
|
|
|
|
g_clear_object (&socket4);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2009-06-15 14:07:13 -04:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 17:31:53 +01:00
|
|
|
|
g_clear_error (&socket6_listen_error);
|
|
|
|
|
g_clear_error (&socket4_listen_error);
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
if ((socket4 != NULL || socket6 != NULL) &&
|
|
|
|
|
G_SOCKET_LISTENER_GET_CLASS (listener)->changed)
|
|
|
|
|
G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener);
|
|
|
|
|
|
2025-07-25 15:31:53 +01:00
|
|
|
|
g_clear_object (&socket6);
|
|
|
|
|
g_clear_object (&socket4);
|
|
|
|
|
|
2009-06-15 14:07:13 -04:00
|
|
|
|
return candidate_port;
|
|
|
|
|
}
|