Blame platform-demos/ca/custom-gsource.c.page

Packit 1470ea
Packit 1470ea
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2003/XInclude" type="guide" style="task" id="custom-gsource.c" xml:lang="ca">
Packit 1470ea
Packit 1470ea
  <info>
Packit 1470ea
    <link type="guide" xref="c#examples"/>
Packit 1470ea
Packit 1470ea
    <credit type="author copyright">
Packit 1470ea
      <name>Philip Withnall</name>
Packit 1470ea
      <email its:translate="no">philip.withnall@collabora.co.uk</email>
Packit 1470ea
      <years>2015</years>
Packit 1470ea
    </credit>
Packit 1470ea
Packit 1470ea
    <include xmlns="http://www.w3.org/2001/XInclude" href="legal.xml"/>
Packit 1470ea
Packit 1470ea
    <desc>
Packit 1470ea
      Tutorial for writing a custom GSource implementation
Packit 1470ea
    </desc>
Packit 1470ea
  </info>
Packit 1470ea
Packit 1470ea
  <title>Custom GSources</title>
Packit 1470ea
Packit 1470ea
  <synopsis>
Packit 1470ea
    <title>Summary</title>
Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      This article is a tutorial on creating a custom GSource. For
Packit 1470ea
      the reference documentation, see the
Packit 1470ea
      <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource">GLib
Packit 1470ea
      API reference</link>.
Packit 1470ea
    

Packit 1470ea
  </synopsis>
Packit 1470ea
Packit 1470ea
  <section id="what-is-gsource">
Packit 1470ea
    <title>What is GSource?</title>
Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      A <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource">GSource</link>
Packit 1470ea
      is an expected event with an associated callback function which will be
Packit 1470ea
      invoked when that event is received. An event could be a timeout or data
Packit 1470ea
      being received on a socket, for example.
Packit 1470ea
    

Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      GLib contains various types of GSource, but also allows
Packit 1470ea
      applications to define their own, allowing custom events to be integrated
Packit 1470ea
      into the main loop.
Packit 1470ea
    

Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      The structure of a GSource and its virtual functions are
Packit 1470ea
      documented in detail in the
Packit 1470ea
      <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSourceFuncs">GLib
Packit 1470ea
      API reference</link>.
Packit 1470ea
    

Packit 1470ea
  </section>
Packit 1470ea
Packit 1470ea
  <section id="queue-source">
Packit 1470ea
    <title>A Message Queue Source</title>
Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      As a running example, a message queue source will be used which dispatches
Packit 1470ea
      its callback whenever a message is enqueued to a queue internal to the
Packit 1470ea
      source (potentially from another thread).
Packit 1470ea
    

Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      This type of source is useful for efficiently transferring large numbers
Packit 1470ea
      of messages between main contexts. The alternative is transferring each
Packit 1470ea
      message as a separate idle GSource using
Packit 1470ea
      g_source_attach(). For large numbers of messages, this means
Packit 1470ea
      a lot of allocations and frees of GSources.
Packit 1470ea
    

Packit 1470ea
Packit 1470ea
    <section id="gsource-structure">
Packit 1470ea
      <title>Structure</title>
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        Firstly, a structure for the source needs to be declared. This must
Packit 1470ea
        contain a GSource as its parent, followed by the private
Packit 1470ea
        fields for the source: the queue and a function to call to free each
Packit 1470ea
        message once finished with.
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
typedef struct {
Packit 1470ea
  GSource         parent;
Packit 1470ea
  GAsyncQueue    *queue;  /* owned */
Packit 1470ea
  GDestroyNotify  destroy_message;
Packit 1470ea
} MessageQueueSource;
Packit 1470ea
    </section>
Packit 1470ea
Packit 1470ea
    <section id="prepare-function">
Packit 1470ea
      <title>Prepare Function</title>
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        Next, the prepare function for the source must be defined. This determines
Packit 1470ea
        whether the source is ready to be dispatched. As this source is using an
Packit 1470ea
        in-memory queue, this can be determined by checking the queue’s length: if
Packit 1470ea
        there are elements in the queue, the source can be dispatched to handle
Packit 1470ea
        them.
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
return (g_async_queue_length (message_queue_source->queue) > 0);
Packit 1470ea
    </section>
Packit 1470ea
Packit 1470ea
    <section id="check-function">
Packit 1470ea
      <title>Check Function</title>
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        As this source has no file descriptors, the prepare and check functions
Packit 1470ea
        essentially have the same job, so a check function is not needed.
Packit 1470ea
        Setting the field to NULL in GSourceFuncs
Packit 1470ea
        bypasses the check function for this source type.
Packit 1470ea
      

Packit 1470ea
    </section>
Packit 1470ea
Packit 1470ea
    <section id="dispatch-function">
Packit 1470ea
      <title>Dispatch Function</title>
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        For this source, the dispatch function is where the complexity lies. It
Packit 1470ea
        needs to dequeue a message from the queue, then pass that message to the
Packit 1470ea
        GSource’s callback function. No messages may be queued: even
Packit 1470ea
        through the prepare function returned true, another source wrapping the
Packit 1470ea
        same queue may have been dispatched in the mean time and taken the final
Packit 1470ea
        message from the queue. Further, if no callback has been set for the
Packit 1470ea
        GSource (which is allowed), the message must be destroyed and
Packit 1470ea
        silently dropped.
Packit 1470ea
      

Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        If both a message and callback are set, the callback can be invoked on the
Packit 1470ea
        message and its return value propagated as the return value of the
Packit 1470ea
        dispatch function. This is FALSE to destroy the
Packit 1470ea
        GSource and TRUE to keep it alive, just as for
Packit 1470ea
        GSourceFunc — these semantics are the same for all dispatch
Packit 1470ea
        function implementations.
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
/* Pop a message off the queue. */
Packit 1470ea
message = g_async_queue_try_pop (message_queue_source->queue);
Packit 1470ea
Packit 1470ea
/* If there was no message, bail. */
Packit 1470ea
if (message == NULL)
Packit 1470ea
  {
Packit 1470ea
    /* Keep the source around to handle the next message. */
Packit 1470ea
    return TRUE;
Packit 1470ea
  }
Packit 1470ea
Packit 1470ea
/* @func may be %NULL if no callback was specified.
Packit 1470ea
 * If so, drop the message. */
Packit 1470ea
if (func == NULL)
Packit 1470ea
  {
Packit 1470ea
    if (message_queue_source->destroy_message != NULL)
Packit 1470ea
      {
Packit 1470ea
        message_queue_source->destroy_message (message);
Packit 1470ea
      }
Packit 1470ea
Packit 1470ea
    /* Keep the source around to consume the next message. */
Packit 1470ea
    return TRUE;
Packit 1470ea
  }
Packit 1470ea
Packit 1470ea
return func (message, user_data);
Packit 1470ea
    </section>
Packit 1470ea
Packit 1470ea
    <section id="callback">
Packit 1470ea
      <title>Callback Functions</title>
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        The callback from a GSource does not have to have type
Packit 1470ea
        GSourceFunc. It can be whatever function type is called in
Packit 1470ea
        the source’s dispatch function, as long as that type is sufficiently
Packit 1470ea
        documented.
Packit 1470ea
      

Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        Normally, g_source_set_callback() is used to set the
Packit 1470ea
        callback function for a source instance. With its
Packit 1470ea
        GDestroyNotify, a strong reference can be held to keep an
Packit 1470ea
        object alive while the source is still alive:
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
g_source_set_callback (source, callback_func,
Packit 1470ea
                       g_object_ref (object_to_strong_ref),
Packit 1470ea
                       (GDestroyNotify) g_object_unref);
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        However, GSource has a layer of indirection for retrieving
Packit 1470ea
        this callback, exposed as g_source_set_callback_indirect().
Packit 1470ea
        This allows GObject to set a GClosure as the callback for a
Packit 1470ea
        source, which allows for sources which are automatically destroyed when
Packit 1470ea
        an object is finalized — a weak reference, in contrast to the
Packit 1470ea
        strong reference above:
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
g_source_set_closure (source,
Packit 1470ea
                      g_cclosure_new_object (callback_func,
Packit 1470ea
                                             object_to_weak_ref));
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        It also allows for a generic, closure-based ‘dummy’ callback, which can
Packit 1470ea
        be used when a source needs to exist but no action needs to be performed
Packit 1470ea
        in its callback:
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
g_source_set_dummy_callback (source);
Packit 1470ea
    </section>
Packit 1470ea
Packit 1470ea
    <section id="constructor">
Packit 1470ea
      <title>Constructor</title>
Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        Finally, the GSourceFuncs definition of the
Packit 1470ea
        GSource can be written, alongside a construction function.
Packit 1470ea
        It is typical practice to expose new source types simply as
Packit 1470ea
        GSources, not as the subtype structure; so the constructor
Packit 1470ea
        returns a GSource*.
Packit 1470ea
      

Packit 1470ea
Packit 1470ea
      

Packit 1470ea
        The example constructor here also demonstrates use of a child source to
Packit 1470ea
        support cancellation conveniently. If the GCancellable is
Packit 1470ea
        cancelled, the application’s callback will be dispatched and can check
Packit 1470ea
        for cancellation. (The application code will need to make a pointer to
Packit 1470ea
        the GCancellable available to its callback, as a field of the
Packit 1470ea
        callback’s user data set in g_source_set_callback()).
Packit 1470ea
      

Packit 1470ea
      
Packit 1470ea
GSource *
Packit 1470ea
message_queue_source_new (GAsyncQueue    *queue,
Packit 1470ea
                          GDestroyNotify  destroy_message,
Packit 1470ea
                          GCancellable   *cancellable)
Packit 1470ea
{
Packit 1470ea
  GSource *source;  /* alias of @message_queue_source */
Packit 1470ea
  MessageQueueSource *message_queue_source;  /* alias of @source */
Packit 1470ea
Packit 1470ea
  g_return_val_if_fail (queue != NULL, NULL);
Packit 1470ea
  g_return_val_if_fail (cancellable == NULL ||
Packit 1470ea
                        G_IS_CANCELLABLE (cancellable), NULL);
Packit 1470ea
Packit 1470ea
  source = g_source_new (&message_queue_source_funcs,
Packit 1470ea
                         sizeof (MessageQueueSource));
Packit 1470ea
  message_queue_source = (MessageQueueSource *) source;
Packit 1470ea
Packit 1470ea
  /* The caller can overwrite this name with something more useful later. */
Packit 1470ea
  g_source_set_name (source, "MessageQueueSource");
Packit 1470ea
Packit 1470ea
  message_queue_source->queue = g_async_queue_ref (queue);
Packit 1470ea
  message_queue_source->destroy_message = destroy_message;
Packit 1470ea
Packit 1470ea
  /* Add a cancellable source. */
Packit 1470ea
  if (cancellable != NULL)
Packit 1470ea
    {
Packit 1470ea
      GSource *cancellable_source;
Packit 1470ea
Packit 1470ea
      cancellable_source = g_cancellable_source_new (cancellable);
Packit 1470ea
      g_source_set_dummy_callback (cancellable_source);
Packit 1470ea
      g_source_add_child_source (source, cancellable_source);
Packit 1470ea
      g_source_unref (cancellable_source);
Packit 1470ea
    }
Packit 1470ea
Packit 1470ea
  return source;
Packit 1470ea
}
Packit 1470ea
    </section>
Packit 1470ea
  </section>
Packit 1470ea
Packit 1470ea
  <section id="full-listing">
Packit 1470ea
    <title>Complete Example</title>
Packit 1470ea
Packit 1470ea
    <listing>
Packit 1470ea
      <title>Complete Example Code</title>
Packit 1470ea
Packit 1470ea
      /**
Packit 1470ea
 * MessageQueueSource:
Packit 1470ea
 *
Packit 1470ea
 * This is a #GSource which wraps a #GAsyncQueue and is dispatched whenever a
Packit 1470ea
 * message can be pulled off the queue. Messages can be enqueued from any
Packit 1470ea
 * thread.
Packit 1470ea
 *
Packit 1470ea
 * The callbacks dispatched by a #MessageQueueSource have type
Packit 1470ea
 * #MessageQueueSourceFunc.
Packit 1470ea
 *
Packit 1470ea
 * #MessageQueueSource supports adding a #GCancellable child source which will
Packit 1470ea
 * additionally dispatch if a provided #GCancellable is cancelled.
Packit 1470ea
 */
Packit 1470ea
typedef struct {
Packit 1470ea
  GSource         parent;
Packit 1470ea
  GAsyncQueue    *queue;  /* owned */
Packit 1470ea
  GDestroyNotify  destroy_message;
Packit 1470ea
} MessageQueueSource;
Packit 1470ea
Packit 1470ea
/**
Packit 1470ea
 * MessageQueueSourceFunc:
Packit 1470ea
 * @message: (transfer full) (nullable): message pulled off the queue
Packit 1470ea
 * @user_data: user data provided to g_source_set_callback()
Packit 1470ea
 *
Packit 1470ea
 * Callback function type for #MessageQueueSource.
Packit 1470ea
 */
Packit 1470ea
typedef gboolean (*MessageQueueSourceFunc) (gpointer message,
Packit 1470ea
                                            gpointer user_data);
Packit 1470ea
Packit 1470ea
static gboolean
Packit 1470ea
message_queue_source_prepare (GSource *source,
Packit 1470ea
                              gint    *timeout_)
Packit 1470ea
{
Packit 1470ea
  MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
Packit 1470ea
Packit 1470ea
  return (g_async_queue_length (message_queue_source->queue) > 0);
Packit 1470ea
}
Packit 1470ea
Packit 1470ea
static gboolean
Packit 1470ea
message_queue_source_dispatch (GSource     *source,
Packit 1470ea
                               GSourceFunc  callback,
Packit 1470ea
                               gpointer     user_data)
Packit 1470ea
{
Packit 1470ea
  MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
Packit 1470ea
  gpointer message;
Packit 1470ea
  MessageQueueSourceFunc func = (MessageQueueSourceFunc) callback;
Packit 1470ea
Packit 1470ea
  /* Pop a message off the queue. */
Packit 1470ea
  message = g_async_queue_try_pop (message_queue_source->queue);
Packit 1470ea
Packit 1470ea
  /* If there was no message, bail. */
Packit 1470ea
  if (message == NULL)
Packit 1470ea
    {
Packit 1470ea
      /* Keep the source around to handle the next message. */
Packit 1470ea
      return TRUE;
Packit 1470ea
    }
Packit 1470ea
Packit 1470ea
  /* @func may be %NULL if no callback was specified.
Packit 1470ea
   * If so, drop the message. */
Packit 1470ea
  if (func == NULL)
Packit 1470ea
    {
Packit 1470ea
      if (message_queue_source->destroy_message != NULL)
Packit 1470ea
        {
Packit 1470ea
          message_queue_source->destroy_message (message);
Packit 1470ea
        }
Packit 1470ea
Packit 1470ea
      /* Keep the source around to consume the next message. */
Packit 1470ea
      return TRUE;
Packit 1470ea
    }
Packit 1470ea
Packit 1470ea
  return func (message, user_data);
Packit 1470ea
}
Packit 1470ea
Packit 1470ea
static void
Packit 1470ea
message_queue_source_finalize (GSource *source)
Packit 1470ea
{
Packit 1470ea
  MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
Packit 1470ea
Packit 1470ea
  g_async_queue_unref (message_queue_source->queue);
Packit 1470ea
}
Packit 1470ea
Packit 1470ea
static gboolean
Packit 1470ea
message_queue_source_closure_callback (gpointer message,
Packit 1470ea
                                       gpointer user_data)
Packit 1470ea
{
Packit 1470ea
  GClosure *closure = user_data;
Packit 1470ea
  GValue param_value = G_VALUE_INIT;
Packit 1470ea
  GValue result_value = G_VALUE_INIT;
Packit 1470ea
  gboolean retval;
Packit 1470ea
Packit 1470ea
  /* The invoked function is responsible for freeing @message. */
Packit 1470ea
  g_value_init (&result_value, G_TYPE_BOOLEAN);
Packit 1470ea
  g_value_init (&param_value, G_TYPE_POINTER);
Packit 1470ea
  g_value_set_pointer (&param_value, message);
Packit 1470ea
Packit 1470ea
  g_closure_invoke (closure, &result_value, 1, &param_value, NULL);
Packit 1470ea
  retval = g_value_get_boolean (&result_value);
Packit 1470ea
Packit 1470ea
  g_value_unset (&param_value);
Packit 1470ea
  g_value_unset (&result_value);
Packit 1470ea
Packit 1470ea
  return retval;
Packit 1470ea
}
Packit 1470ea
Packit 1470ea
static GSourceFuncs message_queue_source_funcs =
Packit 1470ea
  {
Packit 1470ea
    message_queue_source_prepare,
Packit 1470ea
    NULL,  /* check */
Packit 1470ea
    message_queue_source_dispatch,
Packit 1470ea
    message_queue_source_finalize,
Packit 1470ea
    (GSourceFunc) message_queue_source_closure_callback,
Packit 1470ea
    NULL,
Packit 1470ea
  };
Packit 1470ea
Packit 1470ea
/**
Packit 1470ea
 * message_queue_source_new:
Packit 1470ea
 * @queue: the queue to check
Packit 1470ea
 * @destroy_message: (nullable): function to free a message, or %NULL
Packit 1470ea
 * @cancellable: (nullable): a #GCancellable, or %NULL
Packit 1470ea
 *
Packit 1470ea
 * Create a new #MessageQueueSource, a type of #GSource which dispatches for
Packit 1470ea
 * each message queued to it.
Packit 1470ea
 *
Packit 1470ea
 * If a callback function of type #MessageQueueSourceFunc is connected to the
Packit 1470ea
 * returned #GSource using g_source_set_callback(), it will be invoked for each
Packit 1470ea
 * message, with the message passed as its first argument. It is responsible for
Packit 1470ea
 * freeing the message. If no callback is set, messages are automatically freed
Packit 1470ea
 * as they are queued.
Packit 1470ea
 *
Packit 1470ea
 * Returns: (transfer full): a new #MessageQueueSource
Packit 1470ea
 */
Packit 1470ea
GSource *
Packit 1470ea
message_queue_source_new (GAsyncQueue    *queue,
Packit 1470ea
                          GDestroyNotify  destroy_message,
Packit 1470ea
                          GCancellable   *cancellable)
Packit 1470ea
{
Packit 1470ea
  GSource *source;  /* alias of @message_queue_source */
Packit 1470ea
  MessageQueueSource *message_queue_source;  /* alias of @source */
Packit 1470ea
Packit 1470ea
  g_return_val_if_fail (queue != NULL, NULL);
Packit 1470ea
  g_return_val_if_fail (cancellable == NULL ||
Packit 1470ea
                        G_IS_CANCELLABLE (cancellable), NULL);
Packit 1470ea
Packit 1470ea
  source = g_source_new (&message_queue_source_funcs,
Packit 1470ea
                         sizeof (MessageQueueSource));
Packit 1470ea
  message_queue_source = (MessageQueueSource *) source;
Packit 1470ea
Packit 1470ea
  /* The caller can overwrite this name with something more useful later. */
Packit 1470ea
  g_source_set_name (source, "MessageQueueSource");
Packit 1470ea
Packit 1470ea
  message_queue_source->queue = g_async_queue_ref (queue);
Packit 1470ea
  message_queue_source->destroy_message = destroy_message;
Packit 1470ea
Packit 1470ea
  /* Add a cancellable source. */
Packit 1470ea
  if (cancellable != NULL)
Packit 1470ea
    {
Packit 1470ea
      GSource *cancellable_source;
Packit 1470ea
Packit 1470ea
      cancellable_source = g_cancellable_source_new (cancellable);
Packit 1470ea
      g_source_set_dummy_callback (cancellable_source);
Packit 1470ea
      g_source_add_child_source (source, cancellable_source);
Packit 1470ea
      g_source_unref (cancellable_source);
Packit 1470ea
    }
Packit 1470ea
Packit 1470ea
  return source;
Packit 1470ea
}
Packit 1470ea
Packit 1470ea
    </listing>
Packit 1470ea
  </section>
Packit 1470ea
Packit 1470ea
  <section id="further-examples">
Packit 1470ea
    <title>Further Examples</title>
Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      Sources can be more complex than the example given above. In
Packit 1470ea
      <link href="http://nice.freedesktop.org/">libnice</link>, a custom
Packit 1470ea
      GSource is needed to poll a set of sockets which changes
Packit 1470ea
      dynamically. The implementation is given as ComponentSource
Packit 1470ea
      in <link href="http://cgit.freedesktop.org/libnice/libnice/tree/agent/component.c#n941">component.c</link>
Packit 1470ea
      and demonstrates a more complex use of the prepare function.
Packit 1470ea
    

Packit 1470ea
Packit 1470ea
    

Packit 1470ea
      Another example is a custom source to interface GnuTLS with GLib in its
Packit 1470ea
      GTlsConnection implementation.
Packit 1470ea
      <link href="https://git.gnome.org/browse/glib-networking/tree/tls/gnutls/gtlsconnection-gnutls.c#n871">GTlsConnectionGnutlsSource</link>
Packit 1470ea
      synchronizes the main thread and a TLS worker thread which performs the
Packit 1470ea
      blocking TLS operations.
Packit 1470ea
    

Packit 1470ea
  </section>
Packit 1470ea
</page>