* 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>
*/
struct _GCancellablePrivate
{
guint cancelled : 1;
+ guint cancelled_running : 1;
guint cancelled_running_waiting : 1;
- GThread *cancelled_running_thread;
guint fd_refcount;
GWakeup *wakeup;
*
* 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;
* if (cancellable)
* id = g_cancellable_connect (cancellable,
* G_CALLBACK (cancelled_handler)
- * my_data, NULL);
+ * 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);
* ]|
*
*
* 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)
*
* If cancellable is currently in use by any cancellable operation
* then the behavior of this function is undefined.
+ *
+ * Note that it is generally not a good idea to reuse an existing
+ * cancellable for more operations after it has been cancelled once,
+ * as this function might tempt you to do. The recommended practice
+ * is to drop the reference to a cancellable after cancelling it,
+ * and let it die with the outstanding async operations. You should
+ * create a fresh cancellable for further async operations.
**/
void
g_cancellable_reset (GCancellable *cancellable)
priv = cancellable->priv;
- while (priv->cancelled_running_thread != NULL &&
- priv->cancelled_running_thread != g_thread_self ())
+ while (priv->cancelled_running)
{
priv->cancelled_running_waiting = TRUE;
g_cond_wait (&cancellable_cond, &cancellable_mutex);
}
priv->cancelled = TRUE;
- priv->cancelled_running_thread = g_thread_self ();
+ priv->cancelled_running = TRUE;
if (priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
g_mutex_lock (&cancellable_mutex);
- priv->cancelled_running_thread = NULL;
+ priv->cancelled_running = FALSE;
if (priv->cancelled_running_waiting)
g_cond_broadcast (&cancellable_cond);
priv->cancelled_running_waiting = FALSE;
*
* 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.
*
* @handler_id: Handler id of the handler to be disconnected, or %0.
*
* Disconnects a handler from a cancellable instance similar to
- * g_signal_handler_disconnect(). Additionally, in the event that a
- * signal handler is currently running on a different thread, this
- * call will block until the handler has finished.
+ * g_signal_handler_disconnect(). Additionally, in the event that a
+ * signal handler is currently running, this call will block until the
+ * handler has finished. Calling this function from a
+ * #GCancellable::cancelled signal handler will therefore result in a
+ * deadlock.
*
* This avoids a race condition where a thread cancels at the
* same time as the cancellable operation is finished and the
* signal handler is removed. See #GCancellable::cancelled for
* details on how to use this.
*
- * Note, since 2.38 it is safe to call this function from a
- * #GCancellable::cancelled signal handler, something which would
- * previously have caused a deadlock. However, it is not a good idea
- * to disconnect a signal handler from inside its *own* signal handler.
- *
* If @cancellable is %NULL or @handler_id is %0 this function does
* nothing.
*
priv = cancellable->priv;
- while (priv->cancelled_running_thread != NULL &&
- priv->cancelled_running_thread != g_thread_self ())
+ while (priv->cancelled_running)
{
priv->cancelled_running_waiting = TRUE;
g_cond_wait (&cancellable_cond, &cancellable_mutex);
* 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.
+ * The new #GSource will hold a reference to the #GCancellable.
+ *
+ * Returns: (transfer full): the new #GSource.
*
* Since: 2.28
*/
if (cancellable)
{
cancellable_source->cancellable = g_object_ref (cancellable);
+
+ /* We intentionally don't use g_cancellable_connect() here,
+ * because we don't want the "at most once" behavior.
+ */
cancellable_source->cancelled_handler =
- g_cancellable_connect (cancellable,
- G_CALLBACK (cancellable_source_cancelled),
- source,
- NULL);
+ 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;