[glib] gio: Add GDatagramBased interface and rebase GSocket on it



commit 4e631d2e5fd2d7d27b1ca5e2edd6a4d95c83566e
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Fri Jun 12 08:50:42 2015 +0100

    gio: Add GDatagramBased interface and rebase GSocket on it
    
    GDatagramBased is an interface abstracting datagram-based communications
    in the style of the Berkeley sockets API. It may be contrasted to (for
    example) GIOStream, which supports only streaming I/O.
    
    GDatagramBased allows socket-like communications to be done through any
    object, not just a concrete GSocket (which wraps socket()).
    
    This adds the GDatagramBased interface, and implements it in GSocket.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697907

 docs/reference/gio/gio-docs.xml     |    1 +
 docs/reference/gio/gio-sections.txt |   21 ++
 gio/Makefile.am                     |    2 +
 gio/gdatagrambased.c                |  473 +++++++++++++++++++++++++++++++++++
 gio/gdatagrambased.h                |  144 +++++++++++
 gio/gio-autocleanups.h              |    1 +
 gio/gio.h                           |    1 +
 gio/giotypes.h                      |   19 ++
 gio/gsocket.c                       |  144 +++++++++++-
 gio/makefile.msc                    |    1 +
 po/POTFILES.in                      |    1 +
 11 files changed, 807 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index 6de7d09..f322ddb 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -118,6 +118,7 @@
     <chapter id="networking">
       <title>Low-level network support</title>
       <xi:include href="xml/gsocket.xml"/>
+      <xi:include href="xml/gdatagrambased.xml"/>
       <xi:include href="xml/ginetaddress.xml"/>
       <xi:include href="xml/ginetaddressmask.xml"/>
       <xi:include href="xml/gsocketaddress.xml"/>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index f63ddd3..f0a4c49 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1996,6 +1996,27 @@ g_network_service_get_type
 </SECTION>
 
 <SECTION>
+<FILE>gdatagrambased</FILE>
+<TITLE>GDatagramBased</TITLE>
+GDatagramBased
+GDatagramBasedInterface
+GDatagramBasedSourceFunc
+g_datagram_based_receive_messages
+g_datagram_based_send_messages
+g_datagram_based_create_source
+g_datagram_based_condition_check
+g_datagram_based_condition_wait
+<SUBSECTION Standard>
+G_DATAGRAM_BASED
+G_DATAGRAM_BASED_GET_IFACE
+G_IS_DATAGRAM_BASED
+G_TYPE_DATAGRAM_BASED
+G_TYPE_IS_DATAGRAM_BASED
+<SUBSECTION Private>
+g_datagram_based_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gsocket</FILE>
 <TITLE>GSocket</TITLE>
 GSocket
diff --git a/gio/Makefile.am b/gio/Makefile.am
index b7f91cc..5d5d741 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -357,6 +357,7 @@ libgio_2_0_la_SOURCES =             \
        gconverteroutputstream.c        \
        gcredentials.c          \
        gcredentialsprivate.h   \
+       gdatagrambased.c        \
        gdatainputstream.c      \
        gdataoutputstream.c     \
        gdrive.c                \
@@ -548,6 +549,7 @@ gio_headers =                       \
        gconverter.h            \
        gconverterinputstream.h \
        gconverteroutputstream.h        \
+       gdatagrambased.h        \
        gdatainputstream.h      \
        gdataoutputstream.h     \
        gdrive.h                \
diff --git a/gio/gdatagrambased.c b/gio/gdatagrambased.c
new file mode 100644
index 0000000..cd6e481
--- /dev/null
+++ b/gio/gdatagrambased.c
@@ -0,0 +1,473 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2015 Collabora Ltd.
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Philip Withnall <philip withnall collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gdatagrambased.h"
+
+#include "gcancellable.h"
+#include "gioenumtypes.h"
+#include "gioerror.h"
+#include "gnetworkingprivate.h"
+#include "gsocketaddress.h"
+#include "glibintl.h"
+
+/**
+ * SECTION:gdatagrambased
+ * @short_description: Low-level datagram communications interface
+ * @include: gio/gio.h
+ * @see_also: #GSocket, [<gnetworking.h>][gio-gnetworking.h]
+ *
+ * A #GDatagramBased is a networking interface for representing datagram-based
+ * communications. It is a more or less direct mapping of the core parts of the
+ * BSD socket API in a portable GObject interface. It is implemented by
+ * #GSocket, which wraps the UNIX socket API on UNIX and winsock2 on Windows.
+ *
+ * #GDatagramBased is entirely platform independent, and is intended to be used
+ * alongside higher-level networking APIs such as #GIOStream.
+ *
+ * It uses vectored scatter/gather I/O by default, allowing for many messages
+ * to be sent or received in a single call. Where possible, implementations of
+ * the interface should take advantage of vectored I/O to minimise processing
+ * or system calls. For example, #GSocket uses recvmmsg() and sendmmsg() where
+ * possible. Callers should take advantage of scatter/gather I/O (the use of
+ * multiple buffers per message) to avoid unnecessary copying of data to
+ * assemble or disassemble a message.
+ *
+ * Each #GDatagramBased operation has a timeout parameter which may be negative
+ * for blocking behaviour, zero for non-blocking behaviour, or positive for
+ * timeout behaviour. A blocking operation blocks until finished or there is an
+ * error. A non-blocking operation will return immediately with a
+ * %G_IO_ERROR_WOULD_BLOCK error if it cannot make progress. A timeout operation
+ * will block until the operation is complete or the timeout expires; if the
+ * timeout expires it will return what progress it made, or
+ * %G_IO_ERROR_TIMED_OUT if no progress was made. To know when a call would
+ * successfully run you can call g_datagram_based_condition_check() or
+ * g_datagram_based_condition_wait(). You can also use
+ * g_datagram_based_create_source() and attach it to a #GMainContext to get
+ * callbacks when I/O is possible.
+ *
+ * When running a non-blocking operation applications should always be able to
+ * handle getting a %G_IO_ERROR_WOULD_BLOCK error even when some other function
+ * said that I/O was possible. This can easily happen in case of a race
+ * condition in the application, but it can also happen for other reasons. For
+ * instance, on Windows a socket is always seen as writable until a write
+ * returns %G_IO_ERROR_WOULD_BLOCK.
+ *
+ * As with #GSocket, #GDatagramBaseds can be either connection oriented or
+ * connectionless. The interface does not cover connection establishment — use
+ * methods on the underlying type to establish a connection before sending and
+ * receiving data through the #GDatagramBased API. For connectionless socket
+ * types the target/source address is specified or received in each I/O
+ * operation.
+ *
+ * Like most other APIs in GLib, #GDatagramBased is not inherently thread safe.
+ * To use a #GDatagramBased concurrently from multiple threads, you must
+ * implement your own locking.
+ *
+ * Since: 2.48
+ */
+
+G_DEFINE_INTERFACE (GDatagramBased, g_datagram_based, G_TYPE_OBJECT)
+
+static void
+g_datagram_based_default_init (GDatagramBasedInterface *iface)
+{
+  /* Nothing here. */
+}
+
+/**
+ * g_datagram_based_receive_messages:
+ * @datagram_based: a #GDatagramBased
+ * @messages: (array length=num_messages): an array of #GInputMessage structs
+ * @num_messages: the number of elements in @messages
+ * @flags: an int containing #GSocketMsgFlags flags for the overall operation
+ * @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
+ *   to block indefinitely
+ * @cancellable: (allow-none): a %GCancellable
+ * @error: return location for a #GError
+ *
+ * Receive one or more data messages from @datagram_based in one go.
+ *
+ * @messages must point to an array of #GInputMessage structs and
+ * @num_messages must be the length of this array. Each #GInputMessage
+ * contains a pointer to an array of #GInputVector structs describing the
+ * buffers that the data received in each message will be written to.
+ *
+ * @flags modify how all messages are received. The commonly available
+ * arguments for this are available in the #GSocketMsgFlags enum, but the
+ * values there are the same as the system values, and the flags
+ * are passed in as-is, so you can pass in system-specific flags too. These
+ * flags affect the overall receive operation. Flags affecting individual
+ * messages are returned in #GInputMessage.flags.
+ *
+ * The other members of #GInputMessage are treated as described in its
+ * documentation.
+ *
+ * If @timeout is negative the call will block until @num_messages have been
+ * received, the connection is closed remotely (EOS), @cancellable is cancelled,
+ * or an error occurs.
+ *
+ * If @timeout is 0 the call will return up to @num_messages without blocking,
+ * or %G_IO_ERROR_WOULD_BLOCK if no messages are queued in the operating system
+ * to be received.
+ *
+ * If @timeout is positive the call will block on the same conditions as if
+ * @timeout were negative. If the timeout is reached
+ * before any messages are received, %G_IO_ERROR_TIMED_OUT is returned,
+ * otherwise it will return the number of messages received before timing out.
+ * (Note: This is effectively the behaviour of `MSG_WAITFORONE` with
+ * recvmmsg().)
+ *
+ * To be notified when messages are available, wait for the %G_IO_IN condition.
+ * Note though that you may still receive %G_IO_ERROR_WOULD_BLOCK from
+ * g_datagram_based_receive_messages() even if you were previously notified of a
+ * %G_IO_IN condition.
+ *
+ * If the remote peer closes the connection, any messages queued in the
+ * underlying receive buffer will be returned, and subsequent calls to
+ * g_datagram_based_receive_messages() will return 0 (with no error set).
+ *
+ * If the connection is shut down or closed (by calling g_socket_close() or
+ * g_socket_shutdown() with @shutdown_read set, if it’s a #GSocket, for
+ * example), all calls to this function will return %G_IO_ERROR_CLOSED.
+ *
+ * On error -1 is returned and @error is set accordingly. An error will only
+ * be returned if zero messages could be received; otherwise the number of
+ * messages successfully received before the error will be returned. If
+ * @cancellable is cancelled, %G_IO_ERROR_CANCELLED is returned as with any
+ * other error.
+ *
+ * Returns: number of messages received, or -1 on error. Note that the number
+ *     of messages received may be smaller than @num_messages if @timeout is
+ *     zero or positive, if the peer closed the connection, or if @num_messages
+ *     was larger than `UIO_MAXIOV` (1024), in which case the caller may re-try
+ *     to receive the remaining messages.
+ *
+ * Since: 2.48
+ */
+gint
+g_datagram_based_receive_messages (GDatagramBased  *datagram_based,
+                                   GInputMessage   *messages,
+                                   guint            num_messages,
+                                   gint             flags,
+                                   gint64           timeout,
+                                   GCancellable    *cancellable,
+                                   GError         **error)
+{
+  GDatagramBasedInterface *iface;
+  gint retval;
+  GError *child_error = NULL;
+
+  g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), -1);
+  g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
+  g_return_val_if_fail (cancellable == NULL ||
+                        G_IS_CANCELLABLE (cancellable), -1);
+  g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+  iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
+  g_assert (iface->receive_messages != NULL);
+
+  retval = iface->receive_messages (datagram_based, messages, num_messages,
+                                    flags, timeout, cancellable, &child_error);
+
+  /* Postconditions. */
+  g_return_val_if_fail ((retval < 0) == (child_error != NULL), -1);
+  g_return_val_if_fail (timeout == 0 ||
+                        !g_error_matches (child_error, G_IO_ERROR,
+                                          G_IO_ERROR_WOULD_BLOCK), -1);
+  g_return_val_if_fail (timeout > 0 ||
+                        !g_error_matches (child_error, G_IO_ERROR,
+                                          G_IO_ERROR_TIMED_OUT), -1);
+  g_return_val_if_fail (retval < 0 || (guint) retval <= num_messages, -1);
+
+  if (child_error != NULL)
+      g_propagate_error (error, child_error);
+
+  return retval;
+}
+
+/**
+ * g_datagram_based_send_messages:
+ * @datagram_based: a #GDatagramBased
+ * @messages: (array length=num_messages): an array of #GOutputMessage structs
+ * @num_messages: the number of elements in @messages
+ * @flags: an int containing #GSocketMsgFlags flags
+ * @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
+ *   to block indefinitely
+ * @cancellable: (nullable): a %GCancellable
+ * @error: return location for a #GError
+ *
+ * Send one or more data messages from @datagram_based in one go.
+ *
+ * @messages must point to an array of #GOutputMessage structs and
+ * @num_messages must be the length of this array. Each #GOutputMessage
+ * contains an address to send the data to, and a pointer to an array of
+ * #GOutputVector structs to describe the buffers that the data to be sent
+ * for each message will be gathered from.
+ *
+ * @flags modify how the message is sent. The commonly available arguments
+ * for this are available in the #GSocketMsgFlags enum, but the
+ * values there are the same as the system values, and the flags
+ * are passed in as-is, so you can pass in system-specific flags too.
+ *
+ * The other members of #GOutputMessage are treated as described in its
+ * documentation.
+ *
+ * If @timeout is negative the call will block until @num_messages have been
+ * sent, @cancellable is cancelled, or an error occurs.
+ *
+ * If @timeout is 0 the call will send up to @num_messages without blocking,
+ * or will return %G_IO_ERROR_WOULD_BLOCK if there is no space to send messages.
+ *
+ * If @timeout is positive the call will block on the same conditions as if
+ * @timeout were negative. If the timeout is reached before any messages are
+ * sent, %G_IO_ERROR_TIMED_OUT is returned, otherwise it will return the number
+ * of messages sent before timing out.
+ *
+ * To be notified when messages can be sent, wait for the %G_IO_OUT condition.
+ * Note though that you may still receive %G_IO_ERROR_WOULD_BLOCK from
+ * g_datagram_based_send_messages() even if you were previously notified of a
+ * %G_IO_OUT condition. (On Windows in particular, this is very common due to
+ * the way the underlying APIs work.)
+ *
+ * If the connection is shut down or closed (by calling g_socket_close() or
+ * g_socket_shutdown() with @shutdown_write set, if it’s a #GSocket, for
+ * example), all calls to this function will return %G_IO_ERROR_CLOSED.
+ *
+ * On error -1 is returned and @error is set accordingly. An error will only
+ * be returned if zero messages could be sent; otherwise the number of messages
+ * successfully sent before the error will be returned. If @cancellable is
+ * cancelled, %G_IO_ERROR_CANCELLED is returned as with any other error.
+ *
+ * Returns: number of messages sent, or -1 on error. Note that the number of
+ *     messages sent may be smaller than @num_messages if @timeout is zero
+ *     or positive, or if @num_messages was larger than `UIO_MAXIOV` (1024), in
+ *     which case the caller may re-try to send the remaining messages.
+ *
+ * Since: 2.48
+ */
+gint
+g_datagram_based_send_messages (GDatagramBased   *datagram_based,
+                                GOutputMessage   *messages,
+                                guint             num_messages,
+                                gint              flags,
+                                gint64            timeout,
+                                GCancellable     *cancellable,
+                                GError          **error)
+{
+  GDatagramBasedInterface *iface;
+  gint retval;
+  GError *child_error = NULL;
+
+  g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), -1);
+  g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
+  g_return_val_if_fail (cancellable == NULL ||
+                        G_IS_CANCELLABLE (cancellable), -1);
+  g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+  iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
+  g_assert (iface->send_messages != NULL);
+
+  retval = iface->send_messages (datagram_based, messages, num_messages, flags,
+                                 timeout, cancellable, &child_error);
+
+  /* Postconditions. */
+  g_return_val_if_fail ((retval < 0) == (child_error != NULL), -1);
+  g_return_val_if_fail (timeout == 0 ||
+                        !g_error_matches (child_error, G_IO_ERROR,
+                                          G_IO_ERROR_WOULD_BLOCK), -1);
+  g_return_val_if_fail (timeout > 0 ||
+                        !g_error_matches (child_error, G_IO_ERROR,
+                                          G_IO_ERROR_TIMED_OUT), -1);
+  g_return_val_if_fail (retval < 0 || (guint) retval <= num_messages, -1);
+  g_return_val_if_fail (!(timeout < 0 && num_messages > 0) || retval != 0, -1);
+
+  if (child_error != NULL)
+      g_propagate_error (error, child_error);
+
+  return retval;
+}
+
+/**
+ * g_datagram_based_create_source:
+ * @datagram_based: a #GDatagramBased
+ * @condition: a #GIOCondition mask to monitor
+ * @cancellable: (nullable): a #GCancellable
+ *
+ * Creates a #GSource that can be attached to a #GMainContext to monitor for
+ * the availability of the specified @condition on the #GDatagramBased. The
+ * #GSource keeps a reference to the @datagram_based.
+ *
+ * The callback on the source is of the #GDatagramBasedSourceFunc type.
+ *
+ * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition; these
+ * conditions will always be reported in the callback if they are true.
+ *
+ * If non-%NULL, @cancellable can be used to cancel the source, which will
+ * cause the source to trigger, reporting the current condition (which is
+ * likely 0 unless cancellation happened at the same time as a condition
+ * change). You can check for this in the callback using
+ * g_cancellable_is_cancelled().
+ *
+ * Returns: (transfer full): a newly allocated #GSource
+ *
+ * Since: 2.48
+ */
+GSource *
+g_datagram_based_create_source (GDatagramBased *datagram_based,
+                                GIOCondition    condition,
+                                GCancellable   *cancellable)
+{
+  GDatagramBasedInterface *iface;
+
+  g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), NULL);
+  g_return_val_if_fail (cancellable == NULL ||
+                        G_IS_CANCELLABLE (cancellable), NULL);
+
+  iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
+  g_assert (iface->create_source != NULL);
+
+  return iface->create_source (datagram_based, condition, cancellable);
+}
+
+/**
+ * g_datagram_based_condition_check:
+ * @datagram_based: a #GDatagramBased
+ * @condition: a #GIOCondition mask to check
+ *
+ * Checks on the readiness of @datagram_based to perform operations. The
+ * operations specified in @condition are checked for and masked against the
+ * currently-satisfied conditions on @datagram_based. The result is returned.
+ *
+ * %G_IO_IN will be set in the return value if data is available to read with
+ * g_datagram_based_receive_messages(), or if the connection is closed remotely
+ * (EOS); and if the datagram_based has not been closed locally using some
+ * implementation-specific method (such as g_socket_close() or
+ * g_socket_shutdown() with @shutdown_read set, if it’s a #GSocket).
+ *
+ * If the connection is shut down or closed (by calling g_socket_close() or
+ * g_socket_shutdown() with @shutdown_read set, if it’s a #GSocket, for
+ * example), all calls to this function will return %G_IO_ERROR_CLOSED.
+ *
+ * %G_IO_OUT will be set if it is expected that at least one byte can be sent
+ * using g_datagram_based_send_messages() without blocking. It will not be set
+ * if the datagram_based has been closed locally.
+ *
+ * %G_IO_HUP will be set if the connection has been closed locally.
+ *
+ * %G_IO_ERR will be set if there was an asynchronous error in transmitting data
+ * previously enqueued using g_datagram_based_send_messages().
+ *
+ * Note that on Windows, it is possible for an operation to return
+ * %G_IO_ERROR_WOULD_BLOCK even immediately after
+ * g_datagram_based_condition_check() has claimed that the #GDatagramBased is
+ * ready for writing. Rather than calling g_datagram_based_condition_check() and
+ * then writing to the #GDatagramBased if it succeeds, it is generally better to
+ * simply try writing right away, and try again later if the initial attempt
+ * returns %G_IO_ERROR_WOULD_BLOCK.
+ *
+ * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition; these
+ * conditions will always be set in the output if they are true. Apart from
+ * these flags, the output is guaranteed to be masked by @condition.
+ *
+ * This call never blocks.
+ *
+ * Returns: the #GIOCondition mask of the current state
+ *
+ * Since: 2.48
+ */
+GIOCondition
+g_datagram_based_condition_check (GDatagramBased *datagram_based,
+                                  GIOCondition    condition)
+{
+  GDatagramBasedInterface *iface;
+  GIOCondition out;
+
+  g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), 0);
+
+  iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
+  g_assert (iface->condition_check != NULL);
+
+  out = iface->condition_check (datagram_based, condition);
+
+  /* Postconditions. G_IO_OUT and G_IO_HUP are mutually exclusive. G_IO_IN and
+   * G_IO_HUP are mutually exclusive. The return value must be a subset of
+   * (condition | G_IO_ERR | G_IO_HUP). */
+  g_return_val_if_fail ((out & (G_IO_OUT | G_IO_HUP)) != (G_IO_OUT | G_IO_HUP),
+                        out & ~G_IO_OUT);
+  g_return_val_if_fail ((out & (G_IO_IN | G_IO_HUP)) != (G_IO_IN | G_IO_HUP),
+                        out & ~G_IO_IN);
+  g_return_val_if_fail ((out & ~(condition | G_IO_ERR | G_IO_HUP)) == 0,
+                        out & (condition | G_IO_ERR | G_IO_HUP));
+
+  return out;
+}
+
+/**
+ * g_datagram_based_condition_wait:
+ * @datagram_based: a #GDatagramBased
+ * @condition: a #GIOCondition mask to wait for
+ * @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
+ *   to block indefinitely
+ * @cancellable: (nullable): a #GCancellable
+ * @error: return location for a #GError
+ *
+ * Waits for up to @timeout microseconds for condition to become true on
+ * @datagram_based. If the condition is met, %TRUE is returned.
+ *
+ * If @cancellable is cancelled before the condition is met, or if @timeout is
+ * reached before the condition is met, then %FALSE is returned and @error is
+ * set appropriately (%G_IO_ERROR_CANCELLED or %G_IO_ERROR_TIMED_OUT).
+ *
+ * Returns: %TRUE if the condition was met, %FALSE otherwise
+ *
+ * Since: 2.48
+ */
+gboolean
+g_datagram_based_condition_wait (GDatagramBased  *datagram_based,
+                                 GIOCondition     condition,
+                                 gint64           timeout,
+                                 GCancellable    *cancellable,
+                                 GError         **error)
+{
+  GDatagramBasedInterface *iface;
+  gboolean out;
+  GError *child_error = NULL;
+
+  g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable),
+                        FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
+  g_assert (iface->condition_wait != NULL);
+
+  out = iface->condition_wait (datagram_based, condition, timeout,
+                               cancellable, &child_error);
+
+  /* Postconditions. */
+  g_return_val_if_fail (out == (child_error == NULL), FALSE);
+
+  if (child_error != NULL)
+    g_propagate_error (error, child_error);
+
+  return out;
+}
diff --git a/gio/gdatagrambased.h b/gio/gdatagrambased.h
new file mode 100644
index 0000000..e5d80a0
--- /dev/null
+++ b/gio/gdatagrambased.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2015 Collabora Ltd.
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Philip Withnall <philip withnall collabora co uk>
+ */
+
+#ifndef __G_DATAGRAM_BASED_H__
+#define __G_DATAGRAM_BASED_H__
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DATAGRAM_BASED             (g_datagram_based_get_type ())
+#define G_DATAGRAM_BASED(inst)            (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+                                           G_TYPE_DATAGRAM_BASED, GDatagramBased))
+#define G_IS_DATAGRAM_BASED(inst)         (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+                                           G_TYPE_DATAGRAM_BASED))
+#define G_DATAGRAM_BASED_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
+                                           G_TYPE_DATAGRAM_BASED, \
+                                           GDatagramBasedInterface))
+#define G_TYPE_IS_DATAGRAM_BASED(type)    (g_type_is_a ((type), \
+                                           G_TYPE_DATAGRAM_BASED))
+
+/**
+ * GDatagramBased:
+ *
+ * Interface for socket-like objects with datagram semantics.
+ *
+ * Since: 2.48
+ */
+typedef struct _GDatagramBasedInterface GDatagramBasedInterface;
+
+/**
+ * GDatagramBasedInterface:
+ * @g_iface: The parent interface.
+ * @receive_messages: Virtual method for g_datagram_based_receive_messages().
+ * @send_messages: Virtual method for g_datagram_based_send_messages().
+ * @create_source: Virtual method for g_datagram_based_create_source().
+ * @condition_check: Virtual method for g_datagram_based_condition_check().
+ * @condition_wait: Virtual method for
+ *   g_datagram_based_condition_wait().
+ *
+ * Provides an interface for socket-like objects which have datagram semantics,
+ * following the Berkeley sockets API. The interface methods are thin wrappers
+ * around the corresponding virtual methods, and no pre-processing of inputs is
+ * implemented — so implementations of this API must handle all functionality
+ * documented in the interface methods.
+ *
+ * Since: 2.48
+ */
+struct _GDatagramBasedInterface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual table */
+  gint          (*receive_messages)     (GDatagramBased       *datagram_based,
+                                         GInputMessage        *messages,
+                                         guint                 num_messages,
+                                         gint                  flags,
+                                         gint64                timeout,
+                                         GCancellable         *cancellable,
+                                         GError              **error);
+  gint          (*send_messages)        (GDatagramBased       *datagram_based,
+                                         GOutputMessage       *messages,
+                                         guint                 num_messages,
+                                         gint                  flags,
+                                         gint64                timeout,
+                                         GCancellable         *cancellable,
+                                         GError              **error);
+
+  GSource      *(*create_source)        (GDatagramBased       *datagram_based,
+                                         GIOCondition          condition,
+                                         GCancellable         *cancellable);
+  GIOCondition  (*condition_check)      (GDatagramBased       *datagram_based,
+                                         GIOCondition          condition);
+  gboolean      (*condition_wait)       (GDatagramBased       *datagram_based,
+                                         GIOCondition          condition,
+                                         gint64                timeout,
+                                         GCancellable         *cancellable,
+                                         GError              **error);
+};
+
+GLIB_AVAILABLE_IN_2_48
+GType
+g_datagram_based_get_type             (void);
+
+GLIB_AVAILABLE_IN_2_48
+gint
+g_datagram_based_receive_messages     (GDatagramBased       *datagram_based,
+                                       GInputMessage        *messages,
+                                       guint                 num_messages,
+                                       gint                  flags,
+                                       gint64                timeout,
+                                       GCancellable         *cancellable,
+                                       GError              **error);
+
+GLIB_AVAILABLE_IN_2_48
+gint
+g_datagram_based_send_messages        (GDatagramBased       *datagram_based,
+                                       GOutputMessage       *messages,
+                                       guint                 num_messages,
+                                       gint                  flags,
+                                       gint64                timeout,
+                                       GCancellable         *cancellable,
+                                       GError              **error);
+
+GLIB_AVAILABLE_IN_2_48
+GSource *
+g_datagram_based_create_source        (GDatagramBased       *datagram_based,
+                                       GIOCondition          condition,
+                                       GCancellable         *cancellable);
+GLIB_AVAILABLE_IN_2_48
+GIOCondition
+g_datagram_based_condition_check      (GDatagramBased       *datagram_based,
+                                       GIOCondition          condition);
+GLIB_AVAILABLE_IN_2_48
+gboolean
+g_datagram_based_condition_wait       (GDatagramBased       *datagram_based,
+                                       GIOCondition          condition,
+                                       gint64                timeout,
+                                       GCancellable         *cancellable,
+                                       GError              **error);
+
+G_END_DECLS
+
+#endif /* __G_DATAGRAM_BASED_H__ */
diff --git a/gio/gio-autocleanups.h b/gio/gio-autocleanups.h
index a95ba65..60ab3ba 100644
--- a/gio/gio-autocleanups.h
+++ b/gio/gio-autocleanups.h
@@ -39,6 +39,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterInputStream, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCredentials, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDatagramBased, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataOutputStream, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusActionGroup, g_object_unref)
diff --git a/gio/gio.h b/gio/gio.h
index 9c4ee12..c5cab9e 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -44,6 +44,7 @@
 #include <gio/gconverterinputstream.h>
 #include <gio/gconverteroutputstream.h>
 #include <gio/gcredentials.h>
+#include <gio/gdatagrambased.h>
 #include <gio/gdatainputstream.h>
 #include <gio/gdataoutputstream.h>
 #include <gio/gdbusaddress.h>
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 65aa550..ae7f246 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -40,6 +40,7 @@ typedef struct _GCharsetConverter             GCharsetConverter;
 typedef struct _GConverter                    GConverter;
 typedef struct _GConverterInputStream         GConverterInputStream;
 typedef struct _GConverterOutputStream        GConverterOutputStream;
+typedef struct _GDatagramBased                GDatagramBased;
 typedef struct _GDataInputStream              GDataInputStream;
 typedef struct _GSimplePermission             GSimplePermission;
 typedef struct _GZlibCompressor               GZlibCompressor;
@@ -390,6 +391,24 @@ typedef gboolean (*GSocketSourceFunc) (GSocket *socket,
                                       gpointer user_data);
 
 /**
+ * GDatagramBasedSourceFunc:
+ * @datagram_based: the #GDatagramBased
+ * @condition: the current condition at the source fired
+ * @user_data: data passed in by the user
+ *
+ * This is the function type of the callback used for the #GSource
+ * returned by g_datagram_based_create_source().
+ *
+ * Returns: %G_SOURCE_REMOVE if the source should be removed,
+ *   %G_SOURCE_CONTINUE otherwise
+ *
+ * Since: 2.48
+ */
+typedef gboolean (*GDatagramBasedSourceFunc) (GDatagramBased *datagram_based,
+                                              GIOCondition    condition,
+                                              gpointer        user_data);
+
+/**
  * GInputVector:
  * @buffer: Pointer to a buffer where data will be written.
  * @size: the available size in @buffer.
diff --git a/gio/gsocket.c b/gio/gsocket.c
index 2a1050f..af08040 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -53,6 +53,7 @@
 #endif
 
 #include "gcancellable.h"
+#include "gdatagrambased.h"
 #include "gioenumtypes.h"
 #include "ginetaddress.h"
 #include "ginitable.h"
@@ -137,6 +138,32 @@ static gboolean g_socket_initable_init       (GInitable       *initable,
                                              GCancellable    *cancellable,
                                              GError         **error);
 
+static void     g_socket_datagram_based_iface_init       (GDatagramBasedInterface *iface);
+static gint     g_socket_datagram_based_receive_messages (GDatagramBased  *self,
+                                                          GInputMessage   *messages,
+                                                          guint            num_messages,
+                                                          gint             flags,
+                                                          gint64           timeout,
+                                                          GCancellable    *cancellable,
+                                                          GError         **error);
+static gint     g_socket_datagram_based_send_messages    (GDatagramBased  *self,
+                                                          GOutputMessage  *messages,
+                                                          guint            num_messages,
+                                                          gint             flags,
+                                                          gint64           timeout,
+                                                          GCancellable    *cancellable,
+                                                          GError         **error);
+static GSource *g_socket_datagram_based_create_source    (GDatagramBased           *self,
+                                                          GIOCondition              condition,
+                                                          GCancellable             *cancellable);
+static GIOCondition g_socket_datagram_based_condition_check      (GDatagramBased   *datagram_based,
+                                                                  GIOCondition      condition);
+static gboolean     g_socket_datagram_based_condition_wait       (GDatagramBased   *datagram_based,
+                                                                  GIOCondition      condition,
+                                                                  gint64            timeout,
+                                                                  GCancellable     *cancellable,
+                                                                  GError          **error);
+
 static GSocketAddress *
 cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len);
 
@@ -241,7 +268,9 @@ G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT,
                          G_ADD_PRIVATE (GSocket)
                         g_networking_init ();
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
-                                               g_socket_initable_iface_init));
+                                               g_socket_initable_iface_init);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED,
+                                                g_socket_datagram_based_iface_init));
 
 static int
 get_socket_errno (void)
@@ -1008,6 +1037,16 @@ g_socket_initable_iface_init (GInitableIface *iface)
 }
 
 static void
+g_socket_datagram_based_iface_init (GDatagramBasedInterface *iface)
+{
+  iface->receive_messages = g_socket_datagram_based_receive_messages;
+  iface->send_messages = g_socket_datagram_based_send_messages;
+  iface->create_source = g_socket_datagram_based_create_source;
+  iface->condition_check = g_socket_datagram_based_condition_check;
+  iface->condition_wait = g_socket_datagram_based_condition_wait;
+}
+
+static void
 g_socket_init (GSocket *socket)
 {
   socket->priv = g_socket_get_instance_private (socket);
@@ -1053,6 +1092,109 @@ g_socket_initable_init (GInitable *initable,
   return TRUE;
 }
 
+static gboolean
+check_datagram_based (GDatagramBased  *self,
+                      GError         **error)
+{
+  switch (g_socket_get_socket_type (G_SOCKET (self)))
+    {
+    case G_SOCKET_TYPE_INVALID:
+    case G_SOCKET_TYPE_STREAM:
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   _("Cannot use datagram operations on a non-datagram "
+                     "socket."));
+      return FALSE;
+    case G_SOCKET_TYPE_DATAGRAM:
+    case G_SOCKET_TYPE_SEQPACKET:
+      /* Fall through. */
+      break;
+    }
+
+  /* Due to us sharing #GSocketSource with the #GSocket implementation, it is
+   * pretty tricky to split out #GSocket:timeout so that it does not affect
+   * #GDatagramBased operations (but still affects #GSocket operations). It is
+   * not worth that effort — just disallow it and require the user to specify
+   * timeouts on a per-operation basis. */
+  if (g_socket_get_timeout (G_SOCKET (self)) != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   _("Cannot use datagram operations on a socket with a "
+                     "timeout set."));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gint
+g_socket_datagram_based_receive_messages (GDatagramBased  *self,
+                                          GInputMessage   *messages,
+                                          guint            num_messages,
+                                          gint             flags,
+                                          gint64           timeout,
+                                          GCancellable    *cancellable,
+                                          GError         **error)
+{
+  if (!check_datagram_based (self, error))
+    return FALSE;
+
+  return g_socket_receive_messages_with_timeout (G_SOCKET (self), messages,
+                                                 num_messages, flags, timeout,
+                                                 cancellable, error);
+}
+
+static gint
+g_socket_datagram_based_send_messages (GDatagramBased  *self,
+                                       GOutputMessage  *messages,
+                                       guint            num_messages,
+                                       gint             flags,
+                                       gint64           timeout,
+                                       GCancellable    *cancellable,
+                                       GError         **error)
+{
+  if (!check_datagram_based (self, error))
+    return FALSE;
+
+  return g_socket_send_messages_with_timeout (G_SOCKET (self), messages,
+                                              num_messages, flags, timeout,
+                                              cancellable, error);
+}
+
+static GSource *
+g_socket_datagram_based_create_source (GDatagramBased  *self,
+                                       GIOCondition     condition,
+                                       GCancellable    *cancellable)
+{
+  if (!check_datagram_based (self, NULL))
+    return NULL;
+
+  return g_socket_create_source (G_SOCKET (self), condition, cancellable);
+}
+
+static GIOCondition
+g_socket_datagram_based_condition_check (GDatagramBased  *datagram_based,
+                                         GIOCondition     condition)
+{
+  if (!check_datagram_based (datagram_based, NULL))
+    return G_IO_ERR;
+
+  return g_socket_condition_check (G_SOCKET (datagram_based), condition);
+}
+
+static gboolean
+g_socket_datagram_based_condition_wait (GDatagramBased  *datagram_based,
+                                        GIOCondition     condition,
+                                        gint64           timeout,
+                                        GCancellable    *cancellable,
+                                        GError         **error)
+{
+  if (!check_datagram_based (datagram_based, error))
+    return FALSE;
+
+  return g_socket_condition_timed_wait (G_SOCKET (datagram_based), condition,
+                                        timeout, cancellable, error);
+}
+
 /**
  * g_socket_new:
  * @family: the socket family to use, e.g. %G_SOCKET_FAMILY_IPV4.
diff --git a/gio/makefile.msc b/gio/makefile.msc
index c545578..41a1f89 100644
--- a/gio/makefile.msc
+++ b/gio/makefile.msc
@@ -83,6 +83,7 @@ OBJECTS = \
        gbufferedoutputstream.obj \
        gcancellable.obj \
        gcontenttype.obj \
+       gdatagrambased.obj \
        gdatainputstream.obj \
        gdataoutputstream.obj \
 #      gdesktopappinfo.obj \
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3f111a5..dfa46ec 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -18,6 +18,7 @@ gio/gconverter.c
 gio/gconverterinputstream.c
 gio/gconverteroutputstream.c
 gio/gcredentials.c
+gio/gdatagrambased.c
 gio/gdatainputstream.c
 gio/gdataoutputstream.c
 gio/gdbusaddress.c



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