* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
static guint signals[LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (GCancellable, g_cancellable, G_TYPE_OBJECT);
+G_DEFINE_TYPE_WITH_PRIVATE (GCancellable, g_cancellable, G_TYPE_OBJECT)
static GPrivate current_cancellable;
static GMutex cancellable_mutex;
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- g_type_class_add_private (klass, sizeof (GCancellablePrivate));
-
gobject_class->finalize = g_cancellable_finalize;
/**
*
* Note that disconnecting from this signal (or any signal) in a
* multi-threaded program is prone to race conditions. For instance
- * it is possible that a signal handler may be invoked even
- * <emphasis>after</emphasis> a call to
- * g_signal_handler_disconnect() for that handler has already
- * returned.
+ * it is possible that a signal handler may be invoked even after
+ * a call to g_signal_handler_disconnect() for that handler has
+ * already returned.
*
- * There is also a problem when cancellation happen
- * right before connecting to the signal. If this happens the
- * signal will unexpectedly not be emitted, and checking before
- * connecting to the signal leaves a race condition where this is
- * still happening.
+ * There is also a problem when cancellation happens right before
+ * connecting to the signal. If this happens the signal will
+ * unexpectedly not be emitted, and checking before connecting to
+ * the signal leaves a race condition where this is still happening.
*
* In order to make it safe and easy to connect handlers there
* are two helper functions: g_cancellable_connect() and
* like this.
*
* An example of how to us this:
- * |[
- * /<!-- -->* Make sure we don't do any unnecessary work if already cancelled *<!-- -->/
- * if (g_cancellable_set_error_if_cancelled (cancellable))
+ * |[<!-- language="C" -->
+ * // Make sure we don't do unnecessary work if already cancelled
+ * if (g_cancellable_set_error_if_cancelled (cancellable, error))
* return;
*
- * /<!-- -->* Set up all the data needed to be able to
- * * handle cancellation of the operation *<!-- -->/
+ * // Set up all the data needed to be able to handle cancellation
+ * // of the operation
* my_data = my_data_new (...);
*
* id = 0;
* G_CALLBACK (cancelled_handler)
* data, NULL);
*
- * /<!-- -->* cancellable operation here... *<!-- -->/
+ * // cancellable operation here...
*
* g_cancellable_disconnect (cancellable, id);
*
- * /<!-- -->* cancelled_handler is never called after this, it
- * * is now safe to free the data *<!-- -->/
+ * // cancelled_handler is never called after this, it is now safe
+ * // to free the data
* my_data_free (my_data);
* ]|
*
static void
g_cancellable_init (GCancellable *cancellable)
{
- cancellable->priv = G_TYPE_INSTANCE_GET_PRIVATE (cancellable,
- G_TYPE_CANCELLABLE,
- GCancellablePrivate);
+ cancellable->priv = g_cancellable_get_instance_private (cancellable);
}
/**
*
* Gets the top cancellable from the stack.
*
- * Returns: (transfer none): a #GCancellable from the top of the stack, or %NULL
- * if the stack is empty.
+ * Returns: (nullable) (transfer none): a #GCancellable from the top
+ * of the stack, or %NULL if the stack is empty.
**/
GCancellable *
g_cancellable_get_current (void)
*
* See #GCancellable::cancelled for details on how to use this.
*
+ * Since GLib 2.40, the lock protecting @cancellable is not held when
+ * @callback is invoked. This lifts a restriction in place for
+ * earlier GLib versions which now makes it easier to write cleanup
+ * code that unconditionally invokes e.g. g_cancellable_cancel().
+ *
* Returns: The id of the signal handler or 0 if @cancellable has already
* been cancelled.
*
void (*_callback) (GCancellable *cancellable,
gpointer user_data);
+ g_mutex_unlock (&cancellable_mutex);
+
_callback = (void *)callback;
id = 0;
callback, data,
(GClosureNotify) data_destroy_func,
0);
+
+ g_mutex_unlock (&cancellable_mutex);
}
- g_mutex_unlock (&cancellable_mutex);
return id;
}
GSource source;
GCancellable *cancellable;
- GPollFD pollfd;
+ guint cancelled_handler;
} GCancellableSource;
-static gboolean
-cancellable_source_prepare (GSource *source,
- gint *timeout)
-{
- GCancellableSource *cancellable_source = (GCancellableSource *)source;
-
- *timeout = -1;
- return g_cancellable_is_cancelled (cancellable_source->cancellable);
-}
-
-static gboolean
-cancellable_source_check (GSource *source)
+static void
+cancellable_source_cancelled (GCancellable *cancellable,
+ gpointer user_data)
{
- GCancellableSource *cancellable_source = (GCancellableSource *)source;
+ GSource *source = user_data;
- return g_cancellable_is_cancelled (cancellable_source->cancellable);
+ if (!g_source_is_destroyed (source))
+ g_source_set_ready_time (source, 0);
}
static gboolean
GCancellableSourceFunc func = (GCancellableSourceFunc)callback;
GCancellableSource *cancellable_source = (GCancellableSource *)source;
+ g_source_set_ready_time (source, -1);
return (*func) (cancellable_source->cancellable, user_data);
}
GCancellableSource *cancellable_source = (GCancellableSource *)source;
if (cancellable_source->cancellable)
- g_object_unref (cancellable_source->cancellable);
+ {
+ g_cancellable_disconnect (cancellable_source->cancellable,
+ cancellable_source->cancelled_handler);
+ g_object_unref (cancellable_source->cancellable);
+ }
}
static gboolean
static GSourceFuncs cancellable_source_funcs =
{
- cancellable_source_prepare,
- cancellable_source_check,
+ NULL,
+ NULL,
cancellable_source_dispatch,
cancellable_source_finalize,
(GSourceFunc)cancellable_source_closure_callback,
- (GSourceDummyMarshal)g_cclosure_marshal_generic,
};
/**
* For convenience, you can call this with a %NULL #GCancellable,
* in which case the source will never trigger.
*
- * Return value: (transfer full): the new #GSource.
+ * Returns: (transfer full): the new #GSource.
*
* Since: 2.28
*/
g_source_set_name (source, "GCancellable");
cancellable_source = (GCancellableSource *)source;
- if (g_cancellable_make_pollfd (cancellable,
- &cancellable_source->pollfd))
+ if (cancellable)
{
cancellable_source->cancellable = g_object_ref (cancellable);
- g_source_add_poll (source, &cancellable_source->pollfd);
+
+ /* We intentionally don't use g_cancellable_connect() here,
+ * because we don't want the "at most once" behavior.
+ */
+ cancellable_source->cancelled_handler =
+ g_signal_connect (cancellable, "cancelled",
+ G_CALLBACK (cancellable_source_cancelled),
+ source);
+ if (g_cancellable_is_cancelled (cancellable))
+ g_source_set_ready_time (source, 0);
}
return source;