Blob Blame History Raw
<?xml version="1.0" encoding="utf-8"?>
<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="el">

  <info>
    <link type="guide" xref="c#examples"/>

    <credit type="author copyright">
      <name>Philip Withnall</name>
      <email its:translate="no">philip.withnall@collabora.co.uk</email>
      <years>2015</years>
    </credit>

    <include xmlns="http://www.w3.org/2001/XInclude" href="legal.xml"/>

    <desc>Μάθημα για την εγγραφή μιας προσαρμοσμένης υλοποίησης <code>GSource</code></desc>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Ελληνική μεταφραστική ομάδα GNOME</mal:name>
      <mal:email>team@gnome.gr</mal:email>
      <mal:years>2012-2015</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Δημήτρης Σπίγγος</mal:name>
      <mal:email>dmtrs32@gmail.com</mal:email>
      <mal:years>2012, 2013</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Μαρία Θουκιδίδου</mal:name>
      <mal:email>marablack3@gmail.com</mal:email>
      <mal:years>2014</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Θάνος Τρυφωνίδης</mal:name>
      <mal:email>tomtryf@gmail.com</mal:email>
      <mal:years>2014, 2015</mal:years>
    </mal:credit>
  </info>

  <title>Προσαρμοσμένα GSources</title>

  <synopsis>
    <title>Περίληψη</title>

    <p>Αυτό το κείμενο είναι ένα μάθημα για την δημιουργία ενός προσαρμοσμένου <code>GSource</code>. Αν θέλετε να δείτε περισσότερη τεκμηρίωση, επισκεφθείτε την <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource">αναφορά του GLib API</link>.</p>
  </synopsis>

  <section id="what-is-gsource">
    <title>Τι είναι <code>GSource</code>;</title>

    <p>Το <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource"><code>GSource</code></link> είναι ένα γεγονός με μια συσχετισμένη συνάρτηση επανάκλησης η οποία καλείται όταν λαμβάνεται το γεγονός. Το γεγονός αυτό μπορεί να είναι ένα χρονικό όριο ή δεδομένα που λαμβάνονται από μια υποδοχή.</p>

    <p>Το GLib περιλαμβάνει διάφορους τύπους <code>GSource</code>, αλλά επιτρέπει επίσης της εφαρμογές να ορίσουν τα δικά τους, επιτρέποντας προσαρμοσμένα συμβάντα να ενσωματωθούν στον κύριο βρόγχο του προγράμματος.</p>

    <p>Η δομή του <code>GSource</code> και των εικονικών συναρτήσεων περιγράφονται λεπτομερώς στην <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSourceFuncs">αναφορά του GLib API</link>.</p>
  </section>

  <section id="queue-source">
    <title>Πηγή μιας ουράς μηνύματος</title>

    <p>Για παράδειγμα, η πηγή μιας ουράς μηνύματος θα χρησιμοποιηθεί για να σταλθεί η επανάκληση της κάθε φορά που ένα μήνυμα θα μπει στην ουρά της πηγής (συνήθως από άλλο νήμα).</p>

    <p>Αυτός ο τύπος πηγής είναι χρήσιμος για την αποτελεσματική μεταφορά μεγάλου αριθμού μηνυμάτων. Ένας εναλλακτικός τρόπος είναι η μεταφορά κάθε μηνύματος ως ξεχωριστό <code>GSource</code> χρησιμοποιώντας το <code>g_source_attach()</code>. Για μεγάλο αριθμό μηνυμάτων, αυτό σημαίνει πως θα υπάρχουν πολλές λειτουργίες κατανομών και ελευθέρωσης των <code>GSource</code>.</p>

    <section id="gsource-structure">
      <title>Δομή</title>

      <p>Αρχικά, πρέπει να οριστεί μια δομή για τις ανάγκες του κώδικα. Αυτή η δομή πρέπει να περιέχει ένα <code>GSource</code> ως γονικό, ακολουθόμενο από ιδιωτικά πεδία: την ουρά και μια συνάρτηση για να καλεστεί κάθε μήνυμα όταν τελειώσει.</p>
      <code mime="text/x-csrc">
typedef struct {
  GSource         parent;
  GAsyncQueue    *queue;  /* owned */
  GDestroyNotify  destroy_message;
} MessageQueueSource;</code>
    </section>

    <section id="prepare-function">
      <title>Συνάρτηση προετοιμασίας</title>

      <p>Στη συνέχεια, πρέπει να οριστεί η συνάρτηση προετοιμασίας. Αυτή καθορίζει αν ο κώδικας είναι έτοιμος να σταλθεί. Καθώς αυτός ο κώδικας χρησιμοποιεί μια ουρά μνήμης, η ενέργεια μπορεί να καθοριστεί ελέγχοντας το μήκος της ουράς: αν υπάρχουν στοιχεία στην ουρά, τότε ο κώδικας μπορεί να σταλθεί για να τα διαχειριστεί.</p>
      <code mime="text/x-csrc">
return (g_async_queue_length (message_queue_source-&gt;queue) &gt; 0);</code>
    </section>

    <section id="check-function">
      <title>Συνάρτηση ελέγχου</title>

      <p>Καθώς αυτή η πηγή δεν έχει καθόλου περιγραφείς αρχείων, οι συναρτήσεις προετοιμασίας και ελέγχου εκτελούν την ίδια δουλειά, έτσι δεν είναι αναγκαία μια συνάρτηση ελέγχου. Ορίζοντας το πεδίο σε <code>NULL</code> στο <code>GSourceFuncs</code> παρακάμπτεται η συνάρτηση ελέγχου για αυτόν τον τύπο πηγής.</p>
    </section>

    <section id="dispatch-function">
      <title>Συνάρτηση αποστολής</title>

      <p>Η συνάρτηση αποστολής είναι το πιο πολύπλοκο κομμάτι. Πρέπει να πάρει ένα μήνυμα από την ουρά και να το περάσει στην συνάρτηση επανάκλησης του <code>GSource</code>. Δεν είναι απαραίτητο να υπάρχουν μηνύματα στην ουρά: ακόμα και αν η συνάρτηση προετοιμασίας επιστρέψει αληθές, μια άλλη πηγή μπορεί να έχει δεχτεί το τελικό μήνυμα από την ουρά. Επίσης, αν δεν είχε οριστεί μια συνάρτηση επανάκλησης για το <code>GSource</code> (το οποίο είναι αποδεκτό), το μήνυμα πρέπει να καταστραφεί.</p>

      <p>Αν έχει οριστεί ένα μήνυμα και μια επανάκληση, η επανάκληση μπορεί να καλεστεί από το μήνυμα και να επιστρέψει μια τιμή, όπως η τιμή που επιστρέφεται από την συνάρτηση αποστολής. Αυτό ορίζεται ως <code>FALSE</code> για την καταστροφή του <code>GSource</code> και <code>TRUE</code> για να παραμείνει ως έχει, όπως και το <code>GSourceFunc</code> — αυτά παραμένουν τα ίδια για όλες τις υλοποιήσεις μιας συνάρτησης αποστολής.</p>
      <code mime="text/x-csrc">
/* Pop a message off the queue. */
message = g_async_queue_try_pop (message_queue_source-&gt;queue);

/* If there was no message, bail. */
if (message == NULL)
  {
    /* Keep the source around to handle the next message. */
    return TRUE;
  }

/* @func may be %NULL if no callback was specified.
 * If so, drop the message. */
if (func == NULL)
  {
    if (message_queue_source-&gt;destroy_message != NULL)
      {
        message_queue_source-&gt;destroy_message (message);
      }

    /* Keep the source around to consume the next message. */
    return TRUE;
  }

return func (message, user_data);</code>
    </section>

    <section id="callback">
      <title>Συναρτήσεις επανάκλησης</title>

      <p>Η επανάκληση από το <code>GSource</code> δεν είναι αναγκαίο να είναι τύπου <code>GSourceFunc</code>. Μπορεί να είναι οποιουδήποτε τύπου συνάρτηση στην πηγή της συνάρτησης επανάκλησης, εφόσον αυτός ο τύπος είναι επαρκώς τεκμηριωμένος.</p>

      <p>Συνήθως, το <code>g_source_set_callback()</code> χρησιμοποιείται για να οριστεί η συνάρτηση επανάκλησης για ένα στιγμιότυπο πηγής. Με το <code>GDestroyNotify</code> του, μια ισχυρή αναφορά μπορεί να δημιουργηθεί για να κρατήσει ζωντανό το αντικείμενο καθώς η πηγή είναι ακόμα ενεργή:</p>
      <code mime="text/x-csrc">
g_source_set_callback (source, callback_func,
                       g_object_ref (object_to_strong_ref),
                       (GDestroyNotify) g_object_unref);</code>

      <p>Ωστόσο, το <code>GSource</code> διαθέτει μια στρώση για τη ήψη της επανάκλησης, ως <code>g_source_set_callback_indirect()</code>. Αυτό επιτρέπει το GObject να ορίσει ένα <code>GClosure</code> ως επανάκληση για την πηγή, επιτρέποντας τις πηγές να καταστρέφονται αυτόματα όταν οριστοποιηθεί ένα αντικείμενο — μια <em>χαλαρή</em> αναφορά, σε αντίθεση με την <em>ισχυρή</em> αναφορά παραπάνω:</p>
      <code mime="text/x-csrc">
g_source_set_closure (source,
                      g_cclosure_new_object (callback_func,
                                             object_to_weak_ref));</code>

      <p>Επιτρέπει επίσης για μια γενική, εικονική επανάκληση, η οποία μπορεί να χρησιμοποιηθεί όταν μια πηγή χρειάζεται να τερματιστεί αλλά δεν απαιτείται να εκτελεστούν ενέργειες στην επανάκληση της:</p>
      <code mime="text/x-csrc">
g_source_set_dummy_callback (source);</code>
    </section>

    <section id="constructor">
      <title>Κατασκευαστής</title>

      <p>Τέλος, ο ορισμός του <code>GSourceFuncs</code> για το <code>GSource</code> μπορεί να γραφεί μαζί με μια συνάρτηση κατασκευαστή. Είναι κοινή πρακτική να παρουσιάζονται οι νέοι τύποι πηγών ως  <code>GSource</code>, αλλά όχι ως τύπο της δομής, έτσι ώστε ο κατασκευαστής να επιστρέφει το <code>GSource*</code>.</p>

      <p>Το παράδειγμα του κατασκευαστή παρουσιάζει τη χρήση μιας θυγατρικής πηγής για την υποστήριξη της λειτουργίας ακύρωσης. Αν ακυρωθεί το <code>GCancellable</code>, η επανάκλαση της εφαρμογής θα σταλθεί και θα ελέγξει για την ακύρωση. (Ο κώδικας της εφαρμογής θα πρέπει να δημιουργήσει έναν δείκτη στο <code>GCancellable</code> που θα είναι διαθέσιμο στην επανάκληση ως ένα πεδίο για τα δεδομένα του χρήστη στο <code>g_source_set_callback()</code>).</p>
      <code mime="text/x-csrc">
GSource *
message_queue_source_new (GAsyncQueue    *queue,
                          GDestroyNotify  destroy_message,
                          GCancellable   *cancellable)
{
  GSource *source;  /* alias of @message_queue_source */
  MessageQueueSource *message_queue_source;  /* alias of @source */

  g_return_val_if_fail (queue != NULL, NULL);
  g_return_val_if_fail (cancellable == NULL ||
                        G_IS_CANCELLABLE (cancellable), NULL);

  source = g_source_new (&amp;message_queue_source_funcs,
                         sizeof (MessageQueueSource));
  message_queue_source = (MessageQueueSource *) source;

  /* The caller can overwrite this name with something more useful later. */
  g_source_set_name (source, "MessageQueueSource");

  message_queue_source-&gt;queue = g_async_queue_ref (queue);
  message_queue_source-&gt;destroy_message = destroy_message;

  /* Add a cancellable source. */
  if (cancellable != NULL)
    {
      GSource *cancellable_source;

      cancellable_source = g_cancellable_source_new (cancellable);
      g_source_set_dummy_callback (cancellable_source);
      g_source_add_child_source (source, cancellable_source);
      g_source_unref (cancellable_source);
    }

  return source;
}</code>
    </section>
  </section>

  <section id="full-listing">
    <title>Πλήρες παράδειγμα</title>

    <listing>
      <title>Δείγμα πλήρους κώδικα</title>

      <code mime="text/x-csrc">/**
 * MessageQueueSource:
 *
 * This is a #GSource which wraps a #GAsyncQueue and is dispatched whenever a
 * message can be pulled off the queue. Messages can be enqueued from any
 * thread.
 *
 * The callbacks dispatched by a #MessageQueueSource have type
 * #MessageQueueSourceFunc.
 *
 * #MessageQueueSource supports adding a #GCancellable child source which will
 * additionally dispatch if a provided #GCancellable is cancelled.
 */
typedef struct {
  GSource         parent;
  GAsyncQueue    *queue;  /* owned */
  GDestroyNotify  destroy_message;
} MessageQueueSource;

/**
 * MessageQueueSourceFunc:
 * @message: (transfer full) (nullable): message pulled off the queue
 * @user_data: user data provided to g_source_set_callback()
 *
 * Callback function type for #MessageQueueSource.
 */
typedef gboolean (*MessageQueueSourceFunc) (gpointer message,
                                            gpointer user_data);

static gboolean
message_queue_source_prepare (GSource *source,
                              gint    *timeout_)
{
  MessageQueueSource *message_queue_source = (MessageQueueSource *) source;

  return (g_async_queue_length (message_queue_source-&gt;queue) &gt; 0);
}

static gboolean
message_queue_source_dispatch (GSource     *source,
                               GSourceFunc  callback,
                               gpointer     user_data)
{
  MessageQueueSource *message_queue_source = (MessageQueueSource *) source;
  gpointer message;
  MessageQueueSourceFunc func = (MessageQueueSourceFunc) callback;

  /* Pop a message off the queue. */
  message = g_async_queue_try_pop (message_queue_source-&gt;queue);

  /* If there was no message, bail. */
  if (message == NULL)
    {
      /* Keep the source around to handle the next message. */
      return TRUE;
    }

  /* @func may be %NULL if no callback was specified.
   * If so, drop the message. */
  if (func == NULL)
    {
      if (message_queue_source-&gt;destroy_message != NULL)
        {
          message_queue_source-&gt;destroy_message (message);
        }

      /* Keep the source around to consume the next message. */
      return TRUE;
    }

  return func (message, user_data);
}

static void
message_queue_source_finalize (GSource *source)
{
  MessageQueueSource *message_queue_source = (MessageQueueSource *) source;

  g_async_queue_unref (message_queue_source-&gt;queue);
}

static gboolean
message_queue_source_closure_callback (gpointer message,
                                       gpointer user_data)
{
  GClosure *closure = user_data;
  GValue param_value = G_VALUE_INIT;
  GValue result_value = G_VALUE_INIT;
  gboolean retval;

  /* The invoked function is responsible for freeing @message. */
  g_value_init (&amp;result_value, G_TYPE_BOOLEAN);
  g_value_init (&amp;param_value, G_TYPE_POINTER);
  g_value_set_pointer (&amp;param_value, message);

  g_closure_invoke (closure, &amp;result_value, 1, &amp;param_value, NULL);
  retval = g_value_get_boolean (&amp;result_value);

  g_value_unset (&amp;param_value);
  g_value_unset (&amp;result_value);

  return retval;
}

static GSourceFuncs message_queue_source_funcs =
  {
    message_queue_source_prepare,
    NULL,  /* check */
    message_queue_source_dispatch,
    message_queue_source_finalize,
    (GSourceFunc) message_queue_source_closure_callback,
    NULL,
  };

/**
 * message_queue_source_new:
 * @queue: the queue to check
 * @destroy_message: (nullable): function to free a message, or %NULL
 * @cancellable: (nullable): a #GCancellable, or %NULL
 *
 * Create a new #MessageQueueSource, a type of #GSource which dispatches for
 * each message queued to it.
 *
 * If a callback function of type #MessageQueueSourceFunc is connected to the
 * returned #GSource using g_source_set_callback(), it will be invoked for each
 * message, with the message passed as its first argument. It is responsible for
 * freeing the message. If no callback is set, messages are automatically freed
 * as they are queued.
 *
 * Returns: (transfer full): a new #MessageQueueSource
 */
GSource *
message_queue_source_new (GAsyncQueue    *queue,
                          GDestroyNotify  destroy_message,
                          GCancellable   *cancellable)
{
  GSource *source;  /* alias of @message_queue_source */
  MessageQueueSource *message_queue_source;  /* alias of @source */

  g_return_val_if_fail (queue != NULL, NULL);
  g_return_val_if_fail (cancellable == NULL ||
                        G_IS_CANCELLABLE (cancellable), NULL);

  source = g_source_new (&amp;message_queue_source_funcs,
                         sizeof (MessageQueueSource));
  message_queue_source = (MessageQueueSource *) source;

  /* The caller can overwrite this name with something more useful later. */
  g_source_set_name (source, "MessageQueueSource");

  message_queue_source-&gt;queue = g_async_queue_ref (queue);
  message_queue_source-&gt;destroy_message = destroy_message;

  /* Add a cancellable source. */
  if (cancellable != NULL)
    {
      GSource *cancellable_source;

      cancellable_source = g_cancellable_source_new (cancellable);
      g_source_set_dummy_callback (cancellable_source);
      g_source_add_child_source (source, cancellable_source);
      g_source_unref (cancellable_source);
    }

  return source;
}
</code>
    </listing>
  </section>

  <section id="further-examples">
    <title>Περισσότερα παραδείγματα</title>

    <p>Ο κώδικας μπορεί να είναι πιο πολύπλοκος από τα παραπάνω παραδείγματα. Στο <link href="http://nice.freedesktop.org/">libnice</link>, ένα προσαρμοσμένο <code>GSource</code> χρειάζεται για να λαμβάνει μια ομάδα υποδοχών που αλλάζει δυναμικά. Η υλοποίηση του γίνεται με το <code>ComponentSource</code> στο αρχείο <link href="http://cgit.freedesktop.org/libnice/libnice/tree/agent/component.c#n941">component.c</link> και παρουσιάζει μια πιο πολύπλοκη συνάρτηση προετοιμασίας.</p>

    <p>Ένα άλλο παράδειγμα είναι ένας προσαρμοσμένος κώδικας για την επικοινωνία του GnuTLS με το GLib και την υλοποίηση <code>GTlsConnection</code> του. Το <link href="https://git.gnome.org/browse/glib-networking/tree/tls/gnutls/gtlsconnection-gnutls.c#n871"><code>GTlsConnectionGnutlsSource</code></link> συγχρονίζει το κύριο νήμα με ένα νήμα TLS το οποίο εκτελεί τις εργασίες του TLS.</p>
  </section>
</page>