ExoJob and ExoSimpleJob

Jannis Pohlmann jannis at xfce.org
Wed May 6 14:14:42 CEST 2009


Hey guys,

I've merged ExoJob and ExoSimpleJob into exo yesterday. They can be
used to wrap long-running, possibly-blocking operations in order to
execute them in an threaded/asynchronous and object-oriented way.

ExoJob is an abstract class. It establishes the threading environment
for operations to run in based on GIOScheduler. It also provides a
basic set of signals such as "finished", "error", "info-message" and
"percent".

ExoSimpleJob derives from ExoJob and can be used to turn complex
functions into a full-fleshed ExoJob.

Here's an example of how one can use it:

  static gboolean
  find_the_answer_to (ExoJob      *job,
                      GValueArray *params,
                      GError     **error)
  {
    const gchar *what = NULL;
    GError      *err = NULL;
    gint         i;

    g_return_if_fail (EXO_IS_JOB (job), FALSE);
    g_return_if_fail (error == NULL || *error == NULL, FALSE);
    g_return_if_fail (params != NULL && params->n_values == 1, FALSE);
    g_return_if_fail (G_VALUE_HOLDS_STRING (&params->values[0]), FALSE);

    /* check what we are looking for */
    what = g_value_get_string (g_value_array_get_nth (params, 0));

    /* frequent cancellation checks are *very* important */
    if (exo_job_set_error_if_cancelled (job, error))
      return FALSE;

    /* let the user know we've started the operation */
    exo_job_info_message (job, "Computing the answer to %s...",
                          what);

    for (i = 0; err == NULL && i <= 100; ++i)
      if (!exo_job_set_error_if_cancelled (job, &err))
        {
          /* update the progress information */
          exo_job_percent (job, (gdouble) i);

          /* wait for a while */
          sleep (1000);
        }

    if (err != NULL)
      {
        g_error_propagate (error, err);
        return FALSE;
      }
    else
      {
        exo_job_info_message (job, "The answer is: %d", 42);
        return TRUE;
      }
  }

  int
  main (int    argc,
        char **argv)
  {
    GtkWidget *progress_dialog;
    ExoJob    *job;

    /* initialize GTK+ and a progress dialog ... */

    /* wrap the operation with an ExoJob */
    job = exo_simple_job_launch (find_the_answer_to, 1, G_TYPE_STRING,
                                 "life, the universe and everything"));

    /* connect to job signals */
    g_signal_connect (job, "error", G_CALLBACK (show_error), NULL);
    g_signal_connect (job, "finished", G_CALLBACK (answer_found), NULL);
    g_signal_connect (job, "percent", G_CALLBACK (update_progress),
                      progress_dialog);
    g_signal_connect (job, "info-message", G_CALLBACK (update_info),
                      progress_dialog);

    /* run the main loop */
    gtk_main ();

    return EXIT_SUCCESS;
  }

As you can see this is very easy to use. The threaded execution of the
operation is entirely hidden underneath a normal GObject with signals.
The signals are emitted in the main loop of the application so you can
directly access and change GUI elements in the signal handlers. The
sychronizaton of GUI and worker threads is no longer an issue you have
to deal with yourself. 

Jobs can be cancelled using exo_job_cancel() though you're advised to
make sure you disconnect all handlers before destroying a job e.g. by
using this:

  g_signal_handlers_disconnect_matched (object->job,
                                        G_SIGNAL_MATCH_DATA,
                                        0, 0, NULL, NULL, object);

"object" is the user data passed to the signal handlers in this case.

Note: Never emit the "error" signal or "finished" yourself. ExoJob does
this for you. All you need to do is to set the GError in the
ExoJobClass::execute or ExoSimpleJobFunc function (see the example
above for the latter case). Cancellation errors are ignored at the
moment because there's usually no need to display an error dialog if
the job was cancelled by the user's request.

ExoJob can be subclassed to add custom signals or to compute the
percentage based on more complex data within the job. ThunarJob adds
"ask", and and "ask-replace" signals for instance, which require user
interaction for the job to be able to continue. 

If you wonder how this looks like, have a look at ThunarJob:
http://svn.xfce.org/svn/xfce/thunar/branches/migration-to-gio/thunar/thunar-job.h
http://svn.xfce.org/svn/xfce/thunar/branches/migration-to-gio/thunar/thunar-job.c

I can imagine that this is useful for many people, so I thought I'd
share this information with you. Enjoy ;)

Cheers,
Jannis
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: <http://mail.xfce.org/pipermail/xfce4-dev/attachments/20090506/cb1d3d88/attachment.pgp>


More information about the Xfce4-dev mailing list