* Author: Alexander Larsson <alexl@redhat.com>
*/
-#include <config.h>
+#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "gsimpleasyncresult.h"
+#include "gasyncresult.h"
+#include "gcancellable.h"
#include "gioscheduler.h"
#include <gio/gioerror.h>
#include "glibintl.h"
/**
* SECTION:gsimpleasyncresult
* @short_description: Simple asynchronous results implementation
+ * @include: gio/gio.h
* @see_also: #GAsyncResult
*
* Implements #GAsyncResult for simple cases. Most of the time, this
* Because of this, #GSimpleAsyncResult is used throughout GIO for
* handling asynchronous functions.
*
- * GSimpleAsyncResult handles #GAsyncReadyCallback<!-- -->s, error reporting,
- * operation cancellation and the final state of an operation, completely
- * transparent to the application. Results can be returned as a pointer e.g.
- * for functions that return data that is collected asynchronously,
- * a boolean value for checking the success or failure of an operation,
- * or a #gssize for operations which return the number of bytes modified
- * by the operation; all of the simple return cases are covered.
+ * GSimpleAsyncResult handles #GAsyncReadyCallback<!-- -->s, error
+ * reporting, operation cancellation and the final state of an operation,
+ * completely transparent to the application. Results can be returned
+ * as a pointer e.g. for functions that return data that is collected
+ * asynchronously, a boolean value for checking the success or failure
+ * of an operation, or a #gssize for operations which return the number
+ * of bytes modified by the operation; all of the simple return cases
+ * are covered.
*
* Most of the time, an application will not need to know of the details
- * of this API; it is handled transparently, and any necessary operations are
- * handled by #GAsyncResult's interface. However, if implementing a new GIO
- * module, for writing language bindings, or for complex applications that
- * need better control of how asynchronous operations are completed, it is
- * important to understand this functionality.
+ * of this API; it is handled transparently, and any necessary operations
+ * are handled by #GAsyncResult's interface. However, if implementing a
+ * new GIO module, for writing language bindings, or for complex
+ * applications that need better control of how asynchronous operations
+ * are completed, it is important to understand this functionality.
*
- * GSimpleAsyncResults are tagged with the calling function to ensure that
- * asynchronous functions and their finishing functions are used together
- * correctly.
+ * GSimpleAsyncResults are tagged with the calling function to ensure
+ * that asynchronous functions and their finishing functions are used
+ * together correctly.
*
* To create a new #GSimpleAsyncResult, call g_simple_async_result_new().
* If the result needs to be created for a #GError, use
*
* An asynchronous operation can be made to ignore a cancellation event by
* calling g_simple_async_result_set_handle_cancellation() with a
- * #GSimpleAsyncResult for the operation and %FALSE.
+ * #GSimpleAsyncResult for the operation and %FALSE. This is useful for
+ * operations that are dangerous to cancel, such as close (which would
+ * cause a leak if cancelled before being run).
*
- * GSimpleAsyncResult can integrate into GLib's Main Event Loop
- * <!-- TODO: Crosslink -->, or it can use #GThread<!-- -->s if available.
+ * GSimpleAsyncResult can integrate into GLib's event loop, #GMainLoop,
+ * or it can use #GThread<!-- -->s if available.
* g_simple_async_result_complete() will finish an I/O task directly within
* the main event loop. g_simple_async_result_complete_in_idle() will
* integrate the I/O task into the main event loop as an idle function and
if (simple->error)
g_error_free (simple->error);
-
- if (G_OBJECT_CLASS (g_simple_async_result_parent_class)->finalize)
- (*G_OBJECT_CLASS (g_simple_async_result_parent_class)->finalize) (object);
+
+ G_OBJECT_CLASS (g_simple_async_result_parent_class)->finalize (object);
}
static void
/**
* g_simple_async_result_new:
- * @source_object: a #GObject the asynchronous function was called with.
+ * @source_object: a #GObject the asynchronous function was called with,
+ * or %NULL.
* @callback: a #GAsyncReadyCallback.
* @user_data: user data passed to @callback.
* @source_tag: the asynchronous function.
{
GSimpleAsyncResult *simple;
- g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+ g_return_val_if_fail (!source_object || G_IS_OBJECT (source_object), NULL);
simple = g_object_new (G_TYPE_SIMPLE_ASYNC_RESULT, NULL);
simple->callback = callback;
- simple->source_object = g_object_ref (source_object);
+ if (source_object)
+ simple->source_object = g_object_ref (source_object);
+ else
+ simple->source_object = NULL;
simple->user_data = user_data;
simple->source_tag = source_tag;
/**
* g_simple_async_result_new_from_error:
- * @source_object: a #GObject.
+ * @source_object: a #GObject, or %NULL.
* @callback: a #GAsyncReadyCallback.
* @user_data: user data passed to @callback.
* @error: a #GError location.
{
GSimpleAsyncResult *simple;
- g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+ g_return_val_if_fail (!source_object || G_IS_OBJECT (source_object), NULL);
simple = g_simple_async_result_new (source_object,
callback,
/**
* g_simple_async_result_new_error:
- * @source_object: a #GObject.
+ * @source_object: a #GObject, or %NULL.
* @callback: a #GAsyncReadyCallback.
* @user_data: user data passed to @callback.
* @domain: a #GQuark.
GSimpleAsyncResult *simple;
va_list args;
- g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+ g_return_val_if_fail (!source_object || G_IS_OBJECT (source_object), NULL);
g_return_val_if_fail (domain != 0, NULL);
g_return_val_if_fail (format != NULL, NULL);
g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
g_return_if_fail (error != NULL);
+ if (simple->error)
+ g_error_free (simple->error);
simple->error = g_error_copy (error);
simple->failed = TRUE;
}
g_return_if_fail (domain != 0);
g_return_if_fail (format != NULL);
+ if (simple->error)
+ g_error_free (simple->error);
simple->error = _g_error_new_valist (domain, code, format, args);
simple->failed = TRUE;
}
* @simple: a #GSimpleAsyncResult.
*
* Completes an asynchronous I/O job.
+ * Must be called in the main thread, as it invokes the callback that
+ * should be called in the main thread. If you are in a different thread
+ * use g_simple_async_result_complete_in_idle().
**/
void
g_simple_async_result_complete (GSimpleAsyncResult *simple)
typedef struct {
GSimpleAsyncResult *simple;
+ GCancellable *cancellable;
GSimpleAsyncThreadFunc func;
} RunInThreadData;
-static void
-run_in_thread (GIOJob *job,
- GCancellable *c,
- gpointer _data)
+
+static gboolean
+complete_in_idle_cb_for_thread (gpointer _data)
{
RunInThreadData *data = _data;
- GSimpleAsyncResult *simple = data->simple;
+ GSimpleAsyncResult *simple;
+ simple = data->simple;
+
+ if (simple->handle_cancellation &&
+ g_cancellable_is_cancelled (data->cancellable))
+ g_simple_async_result_set_error (simple,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "%s", _("Operation was cancelled"));
+
+ g_simple_async_result_complete (simple);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_object_unref (data->simple);
+ g_free (data);
+
+ return FALSE;
+}
+
+static gboolean
+run_in_thread (GIOSchedulerJob *job,
+ GCancellable *c,
+ gpointer _data)
+{
+ RunInThreadData *data = _data;
+ GSimpleAsyncResult *simple = data->simple;
+ GSource *source;
+ guint id;
+
if (simple->handle_cancellation &&
g_cancellable_is_cancelled (c))
g_simple_async_result_set_error (simple,
G_IO_ERROR,
G_IO_ERROR_CANCELLED,
- _("Operation was cancelled"));
+ "%s", _("Operation was cancelled"));
else
data->func (simple,
simple->source_object,
c);
- g_simple_async_result_complete_in_idle (data->simple);
- g_object_unref (data->simple);
- g_free (data);
+ source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (source, complete_in_idle_cb_for_thread, data, NULL);
+
+ id = g_source_attach (source, NULL);
+ g_source_unref (source);
+
+ return FALSE;
}
/**
data = g_new (RunInThreadData, 1);
data->func = func;
data->simple = g_object_ref (simple);
- g_schedule_io_job (run_in_thread, data, NULL, io_priority, cancellable);
+ data->cancellable = cancellable;
+ if (cancellable)
+ g_object_ref (cancellable);
+ g_io_scheduler_push_job (run_in_thread, data, NULL, io_priority, cancellable);
+}
+
+/**
+ * g_simple_async_result_is_valid:
+ * @result: the #GAsyncResult passed to the _finish function.
+ * @source: the #GObject passed to the _finish function.
+ * @source_tag: the asynchronous function.
+ *
+ * Ensures that the data passed to the _finish function of an async
+ * operation is consistent. Three checks are performed.
+ *
+ * First, @result is checked to ensure that it is really a
+ * #GSimpleAsyncResult. Second, @source is checked to ensure that it
+ * matches the source object of @result. Third, @source_tag is
+ * checked to ensure that it is equal to the source_tag argument given
+ * to g_simple_async_result_new() (which, by convention, is a pointer
+ * to the _async function corresponding to the _finish function from
+ * which this function is called).
+ *
+ * Returns: #TRUE if all checks passed or #FALSE if any failed.
+ **/
+gboolean
+g_simple_async_result_is_valid (GAsyncResult *result,
+ GObject *source,
+ gpointer source_tag)
+{
+ GSimpleAsyncResult *simple;
+ GObject *cmp_source;
+
+ if (!G_IS_SIMPLE_ASYNC_RESULT (result))
+ return FALSE;
+ simple = (GSimpleAsyncResult *)result;
+
+ cmp_source = g_async_result_get_source_object (result);
+ if (cmp_source != source)
+ {
+ g_object_unref (cmp_source);
+ return FALSE;
+ }
+ g_object_unref (cmp_source);
+
+ return source_tag == g_simple_async_result_get_source_tag (simple);
}
/**
* @format: a formatted error reporting string.
* @...: a list of variables to fill in @format.
*
- * Reports an error in an idle function.
+ * Reports an error in an asynchronous function in an idle function by
+ * directly setting the contents of the #GAsyncResult with the given error
+ * information.
**/
void
g_simple_async_report_error_in_idle (GObject *object,
}
/**
- * g_simple_async_report_error_in_idle:
+ * g_simple_async_report_gerror_in_idle:
* @object: a #GObject.
* @callback: a #GAsyncReadyCallback.
* @user_data: user data passed to @callback.
* @error: the #GError to report
*
- * Reports an error in an idle function.
+ * Reports an error in an idle function. Similar to
+ * g_simple_async_report_error_in_idle(), but takes a #GError rather
+ * than building a new one.
**/
void
g_simple_async_report_gerror_in_idle (GObject *object,