[kdbus] KDBUS_ITEM_PAYLOAD_OFF items are (once again) relative to msg header
[platform/upstream/glib.git] / gio / gcancellable.c
index 589b83a..258b88d 100644 (file)
  * 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>
  */
 
 #include "config.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
+#include "glib.h"
 #include <gioerror.h>
-#ifdef G_OS_WIN32
-#include <windows.h>
-#endif
+#include "glib-private.h"
 #include "gcancellable.h"
 #include "glibintl.h"
 
-#include "gioalias.h"
 
 /**
  * SECTION:gcancellable
@@ -49,44 +41,31 @@ enum {
   LAST_SIGNAL
 };
 
-struct _GCancellable
+struct _GCancellablePrivate
 {
-  GObject parent_instance;
-
   guint cancelled : 1;
-  guint allocated_pipe : 1;
   guint cancelled_running : 1;
   guint cancelled_running_waiting : 1;
-  int cancel_pipe[2];
 
-#ifdef G_OS_WIN32
-  HANDLE event;
-#endif
+  guint fd_refcount;
+  GWakeup *wakeup;
 };
 
 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;
+static GCond cancellable_cond;
 
-static GStaticPrivate current_cancellable = G_STATIC_PRIVATE_INIT;
-G_LOCK_DEFINE_STATIC(cancellable);
-static GCond *cancellable_cond = NULL;
-  
 static void
 g_cancellable_finalize (GObject *object)
 {
   GCancellable *cancellable = G_CANCELLABLE (object);
 
-  if (cancellable->cancel_pipe[0] != -1)
-    close (cancellable->cancel_pipe[0]);
-  
-  if (cancellable->cancel_pipe[1] != -1)
-    close (cancellable->cancel_pipe[1]);
-
-#ifdef G_OS_WIN32
-  if (cancellable->event)
-    CloseHandle (cancellable->event);
-#endif
+  if (cancellable->priv->wakeup)
+    GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup);
 
   G_OBJECT_CLASS (g_cancellable_parent_class)->finalize (object);
 }
@@ -96,9 +75,6 @@ g_cancellable_class_init (GCancellableClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-  if (cancellable_cond == NULL && g_thread_supported ())
-    cancellable_cond = g_cond_new ();
-  
   gobject_class->finalize = g_cancellable_finalize;
 
   /**
@@ -114,16 +90,14 @@ g_cancellable_class_init (GCancellableClass *klass)
    *
    * 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
@@ -131,13 +105,13 @@ g_cancellable_class_init (GCancellableClass *klass)
    * 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;
@@ -146,12 +120,12 @@ g_cancellable_class_init (GCancellableClass *klass)
    *                                 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);  
    * ]|
    *
@@ -170,61 +144,10 @@ g_cancellable_class_init (GCancellableClass *klass)
   
 }
 
-#ifndef G_OS_WIN32
-static void
-set_fd_nonblocking (int fd)
-{
-#ifdef F_GETFL
-  glong fcntl_flags;
-  fcntl_flags = fcntl (fd, F_GETFL);
-
-#ifdef O_NONBLOCK
-  fcntl_flags |= O_NONBLOCK;
-#else
-  fcntl_flags |= O_NDELAY;
-#endif
-
-  fcntl (fd, F_SETFL, fcntl_flags);
-#endif
-}
-
-static void
-set_fd_close_exec (int fd)
-{
-  int flags;
-
-  flags = fcntl (fd, F_GETFD, 0);
-  if (flags != -1 && (flags & FD_CLOEXEC) == 0)
-    {
-      flags |= FD_CLOEXEC;
-      fcntl (fd, F_SETFD, flags);
-    }
-}
-
-
-static void
-g_cancellable_open_pipe (GCancellable *cancellable)
-{
-  if (pipe (cancellable->cancel_pipe) == 0)
-    {
-      /* Make them nonblocking, just to be sure we don't block
-       * on errors and stuff
-       */
-      set_fd_nonblocking (cancellable->cancel_pipe[0]);
-      set_fd_nonblocking (cancellable->cancel_pipe[1]);
-      set_fd_close_exec (cancellable->cancel_pipe[0]);
-      set_fd_close_exec (cancellable->cancel_pipe[1]);
-    }
-  else
-    g_warning ("Failed to create pipe for GCancellable. Out of file descriptors?");
-}
-#endif
-
 static void
 g_cancellable_init (GCancellable *cancellable)
 {
-  cancellable->cancel_pipe[0] = -1;
-  cancellable->cancel_pipe[1] = -1;
+  cancellable->priv = g_cancellable_get_instance_private (cancellable);
 }
 
 /**
@@ -237,7 +160,7 @@ g_cancellable_init (GCancellable *cancellable)
  * and pass it to the operations.
  *
  * One #GCancellable can be used in multiple consecutive
- * operations, but not in multiple concurrent operations.
+ * operations or in multiple concurrent operations.
  *  
  * Returns: a #GCancellable.
  **/
@@ -249,10 +172,10 @@ g_cancellable_new (void)
 
 /**
  * g_cancellable_push_current:
- * @cancellable: optional #GCancellable object, %NULL to ignore.
- * 
+ * @cancellable: a #GCancellable object
+ *
  * Pushes @cancellable onto the cancellable stack. The current
- * cancllable can then be recieved using g_cancellable_get_current().
+ * cancellable can then be received using g_cancellable_get_current().
  *
  * This is useful when implementing cancellable operations in
  * code that does not allow you to pass down the cancellable object.
@@ -266,47 +189,47 @@ g_cancellable_push_current (GCancellable *cancellable)
   GSList *l;
 
   g_return_if_fail (cancellable != NULL);
-  
-  l = g_static_private_get (&current_cancellable);
+
+  l = g_private_get (&current_cancellable);
   l = g_slist_prepend (l, cancellable);
-  g_static_private_set (&current_cancellable, l, NULL);
+  g_private_set (&current_cancellable, l);
 }
 
 /**
  * g_cancellable_pop_current:
- * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @cancellable: a #GCancellable object
  *
- * Pops @cancellable off the cancellable stack (verifying that @cancellable 
+ * Pops @cancellable off the cancellable stack (verifying that @cancellable
  * is on the top of the stack).
  **/
 void
 g_cancellable_pop_current (GCancellable *cancellable)
 {
   GSList *l;
-  
-  l = g_static_private_get (&current_cancellable);
-  
+
+  l = g_private_get (&current_cancellable);
+
   g_return_if_fail (l != NULL);
   g_return_if_fail (l->data == cancellable);
 
   l = g_slist_delete_link (l, l);
-  g_static_private_set (&current_cancellable, l, NULL);
+  g_private_set (&current_cancellable, l);
 }
 
 /**
  * g_cancellable_get_current:
- * 
+ *
  * Gets the top cancellable from the stack.
- * 
- * Returns: 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)
 {
   GSList *l;
-  
-  l = g_static_private_get (&current_cancellable);
+
+  l = g_private_get (&current_cancellable);
   if (l == NULL)
     return NULL;
 
@@ -317,68 +240,74 @@ g_cancellable_get_current  (void)
  * g_cancellable_reset:
  * @cancellable: a #GCancellable object.
  * 
- * Resets @cancellable to its uncancelled state. 
+ * Resets @cancellable to its uncancelled state.
+ *
+ * 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)
 {
+  GCancellablePrivate *priv;
+
   g_return_if_fail (G_IS_CANCELLABLE (cancellable));
 
-  G_LOCK(cancellable);
-  
-  if (cancellable->cancelled_running)
+  g_mutex_lock (&cancellable_mutex);
+
+  priv = cancellable->priv;
+
+  while (priv->cancelled_running)
     {
-      g_critical ("Can't reset a cancellable during an active operation");
-      G_UNLOCK(cancellable);
-      return;
+      priv->cancelled_running_waiting = TRUE;
+      g_cond_wait (&cancellable_cond, &cancellable_mutex);
     }
-  
-  if (cancellable->cancelled)
+
+  if (priv->cancelled)
     {
-      char ch;
-      
-    /* Make sure we're not leaving old cancel state around */
-      
-#ifdef G_OS_WIN32
-      if (cancellable->event)
-       ResetEvent (cancellable->event);
-      else
-#endif
-      if (cancellable->cancel_pipe[0] != -1)
-       read (cancellable->cancel_pipe[0], &ch, 1);
-      cancellable->cancelled = FALSE;
+      if (priv->wakeup)
+        GLIB_PRIVATE_CALL (g_wakeup_acknowledge) (priv->wakeup);
+
+      priv->cancelled = FALSE;
     }
-  G_UNLOCK(cancellable);
+
+  g_mutex_unlock (&cancellable_mutex);
 }
 
 /**
  * g_cancellable_is_cancelled:
- * @cancellable: a #GCancellable or NULL.
- * 
+ * @cancellable: (allow-none): a #GCancellable or %NULL
+ *
  * Checks if a cancellable job has been cancelled.
- * 
- * Returns: %TRUE if @cancellable is cancelled, 
- * FALSE if called with %NULL or if item is not cancelled. 
+ *
+ * Returns: %TRUE if @cancellable is cancelled,
+ * FALSE if called with %NULL or if item is not cancelled.
  **/
 gboolean
 g_cancellable_is_cancelled (GCancellable *cancellable)
 {
-  return cancellable != NULL && cancellable->cancelled;
+  return cancellable != NULL && cancellable->priv->cancelled;
 }
 
 /**
  * g_cancellable_set_error_if_cancelled:
- * @cancellable: a #GCancellable object.
- * @error: #GError to append error state to.
- * 
+ * @cancellable: (allow-none): a #GCancellable or %NULL
+ * @error: #GError to append error state to
+ *
  * If the @cancellable is cancelled, sets the error to notify
  * that the operation was cancelled.
- * 
- * Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not.
- **/
+ *
+ * Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not
+ */
 gboolean
 g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
-                                     GError       **error)
+                                      GError       **error)
 {
   if (g_cancellable_is_cancelled (cancellable))
     {
@@ -388,7 +317,7 @@ g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
                            _("Operation was cancelled"));
       return TRUE;
     }
-  
+
   return FALSE;
 }
 
@@ -404,6 +333,10 @@ g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
  * readable status. Reading to unset the readable status is done
  * with g_cancellable_reset().
  * 
+ * After a successful return from this function, you should use 
+ * g_cancellable_release_fd() to free up resources allocated for 
+ * the returned file descriptor.
+ *
  * See also g_cancellable_make_pollfd().
  *
  * Returns: A valid file descriptor. %-1 if the file descriptor 
@@ -412,30 +345,23 @@ g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
 int
 g_cancellable_get_fd (GCancellable *cancellable)
 {
-  int fd;
+  GPollFD pollfd;
+
   if (cancellable == NULL)
-    return -1;
+         return -1;
 
 #ifdef G_OS_WIN32
-  return -1;
+  pollfd.fd = -1;
 #else
-  G_LOCK(cancellable);
-  if (!cancellable->allocated_pipe)
-    {
-      cancellable->allocated_pipe = TRUE;
-      g_cancellable_open_pipe (cancellable);
-    }
-
-  fd = cancellable->cancel_pipe[0];
-  G_UNLOCK(cancellable);
+  g_cancellable_make_pollfd (cancellable, &pollfd);
 #endif
 
-  return fd;
+  return pollfd.fd;
 }
 
 /**
  * g_cancellable_make_pollfd:
- * @cancellable: a #GCancellable.
+ * @cancellable: (allow-none): a #GCancellable or %NULL
  * @pollfd: a pointer to a #GPollFD
  * 
  * Creates a #GPollFD corresponding to @cancellable; this can be passed
@@ -443,29 +369,91 @@ g_cancellable_get_fd (GCancellable *cancellable)
  * for unix systems without a native poll and for portability to
  * windows.
  *
+ * When this function returns %TRUE, you should use 
+ * g_cancellable_release_fd() to free up resources allocated for the 
+ * @pollfd. After a %FALSE return, do not call g_cancellable_release_fd().
+ *
+ * If this function returns %FALSE, either no @cancellable was given or
+ * resource limits prevent this function from allocating the necessary 
+ * structures for polling. (On Linux, you will likely have reached 
+ * the maximum number of file descriptors.) The suggested way to handle
+ * these cases is to ignore the @cancellable.
+ *
  * You are not supposed to read from the fd yourself, just check for
  * readable status. Reading to unset the readable status is done
  * with g_cancellable_reset().
+ *
+ * Returns: %TRUE if @pollfd was successfully initialized, %FALSE on 
+ *          failure to prepare the cancellable.
  * 
+ * Since: 2.22
  **/
-void
+gboolean
 g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd)
 {
+  g_return_val_if_fail (pollfd != NULL, FALSE);
+  if (cancellable == NULL)
+    return FALSE;
+  g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+
+  g_mutex_lock (&cancellable_mutex);
+
+  cancellable->priv->fd_refcount++;
+
+  if (cancellable->priv->wakeup == NULL)
+    {
+      cancellable->priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) ();
+
+      if (cancellable->priv->cancelled)
+        GLIB_PRIVATE_CALL (g_wakeup_signal) (cancellable->priv->wakeup);
+    }
+
+  GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (cancellable->priv->wakeup, pollfd);
+
+  g_mutex_unlock (&cancellable_mutex);
+
+  return TRUE;
+}
+
+/**
+ * g_cancellable_release_fd:
+ * @cancellable: a #GCancellable
+ *
+ * Releases a resources previously allocated by g_cancellable_get_fd()
+ * or g_cancellable_make_pollfd().
+ *
+ * For compatibility reasons with older releases, calling this function 
+ * is not strictly required, the resources will be automatically freed
+ * when the @cancellable is finalized. However, the @cancellable will
+ * block scarce file descriptors until it is finalized if this function
+ * is not called. This can cause the application to run out of file 
+ * descriptors when many #GCancellables are used at the same time.
+ * 
+ * Since: 2.22
+ **/
+void
+g_cancellable_release_fd (GCancellable *cancellable)
+{
+  GCancellablePrivate *priv;
+
+  if (cancellable == NULL)
+    return;
+
   g_return_if_fail (G_IS_CANCELLABLE (cancellable));
-  g_return_if_fail (pollfd != NULL);
+  g_return_if_fail (cancellable->priv->fd_refcount > 0);
 
-#ifdef G_OS_WIN32
-  if (!cancellable->event)
+  priv = cancellable->priv;
+
+  g_mutex_lock (&cancellable_mutex);
+
+  priv->fd_refcount--;
+  if (priv->fd_refcount == 0)
     {
-      /* A manual reset anonymous event, starting unset */
-      cancellable->event = CreateEvent (NULL, TRUE, FALSE, NULL);
+      GLIB_PRIVATE_CALL (g_wakeup_free) (priv->wakeup);
+      priv->wakeup = NULL;
     }
-  pollfd->fd = (gintptr)cancellable->event;
-#else /* !G_OS_WIN32 */
-  pollfd->fd = g_cancellable_get_fd (cancellable);
-#endif /* G_OS_WIN32 */
-  pollfd->events = G_IO_IN;
-  pollfd->revents = 0;
+
+  g_mutex_unlock (&cancellable_mutex);
 }
 
 /**
@@ -490,43 +478,43 @@ g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd)
 void
 g_cancellable_cancel (GCancellable *cancellable)
 {
-  gboolean cancel;
+  GCancellablePrivate *priv;
+
+  if (cancellable == NULL ||
+      cancellable->priv->cancelled)
+    return;
 
-  cancel = FALSE;
+  priv = cancellable->priv;
 
-  G_LOCK(cancellable);
-  if (cancellable != NULL &&
-      !cancellable->cancelled)
+  g_mutex_lock (&cancellable_mutex);
+
+  if (priv->cancelled)
     {
-      char ch = 'x';
-      cancel = TRUE;
-      cancellable->cancelled = TRUE;
-      cancellable->cancelled_running = TRUE;
-#ifdef G_OS_WIN32
-      if (cancellable->event)
-       SetEvent(cancellable->event);
-#endif
-      if (cancellable->cancel_pipe[1] != -1)
-       write (cancellable->cancel_pipe[1], &ch, 1);
+      g_mutex_unlock (&cancellable_mutex);
+      return;
     }
-  G_UNLOCK(cancellable);
 
-  if (cancel)
-    {
-      g_object_ref (cancellable);
-      g_signal_emit (cancellable, signals[CANCELLED], 0);
+  priv->cancelled = TRUE;
+  priv->cancelled_running = TRUE;
 
-      G_LOCK(cancellable);
+  if (priv->wakeup)
+    GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
 
-      cancellable->cancelled_running = FALSE;
-      if (cancellable->cancelled_running_waiting)
-       g_cond_broadcast (cancellable_cond);
-      cancellable->cancelled_running_waiting = FALSE;
+  g_mutex_unlock (&cancellable_mutex);
 
-      G_UNLOCK(cancellable);
+  g_object_ref (cancellable);
+  g_signal_emit (cancellable, signals[CANCELLED], 0);
 
-      g_object_unref (cancellable);
-    }
+  g_mutex_lock (&cancellable_mutex);
+
+  priv->cancelled_running = FALSE;
+  if (priv->cancelled_running_waiting)
+    g_cond_broadcast (&cancellable_cond);
+  priv->cancelled_running_waiting = FALSE;
+
+  g_mutex_unlock (&cancellable_mutex);
+
+  g_object_unref (cancellable);
 }
 
 /**
@@ -534,7 +522,7 @@ g_cancellable_cancel (GCancellable *cancellable)
  * @cancellable: A #GCancellable.
  * @callback: The #GCallback to connect.
  * @data: Data to pass to @callback.
- * @data_destroy_func: Free function for @data or %NULL.
+ * @data_destroy_func: (allow-none): Free function for @data or %NULL.
  *
  * Convenience function to connect to the #GCancellable::cancelled
  * signal. Also handles the race condition that may happen
@@ -550,6 +538,11 @@ g_cancellable_cancel (GCancellable *cancellable)
  *
  * 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.
  *
@@ -565,13 +558,15 @@ g_cancellable_connect (GCancellable   *cancellable,
 
   g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), 0);
 
-  G_LOCK (cancellable);
+  g_mutex_lock (&cancellable_mutex);
 
-  if (cancellable->cancelled)
+  if (cancellable->priv->cancelled)
     {
       void (*_callback) (GCancellable *cancellable,
                          gpointer      user_data);
 
+      g_mutex_unlock (&cancellable_mutex);
+
       _callback = (void *)callback;
       id = 0;
 
@@ -586,20 +581,25 @@ g_cancellable_connect (GCancellable   *cancellable,
                                   callback, data,
                                   (GClosureNotify) data_destroy_func,
                                   0);
+
+      g_mutex_unlock (&cancellable_mutex);
     }
-  G_UNLOCK (cancellable);
+
 
   return id;
 }
 
 /**
  * g_cancellable_disconnect:
- * @cancellable: A #GCancellable or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
  * @handler_id: Handler id of the handler to be disconnected, or %0.
  *
- * Disconnects a handler from an cancellable instance similar to
- * g_signal_handler_disconnect() but ensures that once this
- * function returns the handler will not run anymore in any thread.
+ * Disconnects a handler from a cancellable instance similar to
+ * 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
@@ -615,20 +615,143 @@ void
 g_cancellable_disconnect (GCancellable  *cancellable,
                          gulong         handler_id)
 {
+  GCancellablePrivate *priv;
+
   if (handler_id == 0 ||  cancellable == NULL)
     return;
 
-  G_LOCK (cancellable);
-  while (cancellable->cancelled_running)
+  g_mutex_lock (&cancellable_mutex);
+
+  priv = cancellable->priv;
+
+  while (priv->cancelled_running)
     {
-      cancellable->cancelled_running_waiting = TRUE;
-      g_cond_wait (cancellable_cond,
-                   g_static_mutex_get_mutex (& G_LOCK_NAME (cancellable)));
+      priv->cancelled_running_waiting = TRUE;
+      g_cond_wait (&cancellable_cond, &cancellable_mutex);
     }
 
   g_signal_handler_disconnect (cancellable, handler_id);
-  G_UNLOCK (cancellable);
+
+  g_mutex_unlock (&cancellable_mutex);
 }
 
-#define __G_CANCELLABLE_C__
-#include "gioaliasdef.c"
+typedef struct {
+  GSource       source;
+
+  GCancellable *cancellable;
+  guint         cancelled_handler;
+} GCancellableSource;
+
+static void
+cancellable_source_cancelled (GCancellable *cancellable,
+                             gpointer      user_data)
+{
+  GSource *source = user_data;
+
+  if (!g_source_is_destroyed (source))
+    g_source_set_ready_time (source, 0);
+}
+
+static gboolean
+cancellable_source_dispatch (GSource     *source,
+                            GSourceFunc  callback,
+                            gpointer     user_data)
+{
+  GCancellableSourceFunc func = (GCancellableSourceFunc)callback;
+  GCancellableSource *cancellable_source = (GCancellableSource *)source;
+
+  g_source_set_ready_time (source, -1);
+  return (*func) (cancellable_source->cancellable, user_data);
+}
+
+static void
+cancellable_source_finalize (GSource *source)
+{
+  GCancellableSource *cancellable_source = (GCancellableSource *)source;
+
+  if (cancellable_source->cancellable)
+    {
+      g_cancellable_disconnect (cancellable_source->cancellable,
+                                cancellable_source->cancelled_handler);
+      g_object_unref (cancellable_source->cancellable);
+    }
+}
+
+static gboolean
+cancellable_source_closure_callback (GCancellable *cancellable,
+                                    gpointer      data)
+{
+  GClosure *closure = data;
+
+  GValue params = G_VALUE_INIT;
+  GValue result_value = G_VALUE_INIT;
+  gboolean result;
+
+  g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+  g_value_init (&params, G_TYPE_CANCELLABLE);
+  g_value_set_object (&params, cancellable);
+
+  g_closure_invoke (closure, &result_value, 1, &params, NULL);
+
+  result = g_value_get_boolean (&result_value);
+  g_value_unset (&result_value);
+  g_value_unset (&params);
+
+  return result;
+}
+
+static GSourceFuncs cancellable_source_funcs =
+{
+  NULL,
+  NULL,
+  cancellable_source_dispatch,
+  cancellable_source_finalize,
+  (GSourceFunc)cancellable_source_closure_callback,
+};
+
+/**
+ * g_cancellable_source_new: (skip)
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ *
+ * Creates a source that triggers if @cancellable is cancelled and
+ * calls its callback of type #GCancellableSourceFunc. This is
+ * primarily useful for attaching to another (non-cancellable) source
+ * with g_source_add_child_source() to add cancellability to it.
+ *
+ * For convenience, you can call this with a %NULL #GCancellable,
+ * in which case the source will never trigger.
+ *
+ * The new #GSource will hold a reference to the #GCancellable.
+ *
+ * Returns: (transfer full): the new #GSource.
+ *
+ * Since: 2.28
+ */
+GSource *
+g_cancellable_source_new (GCancellable *cancellable)
+{
+  GSource *source;
+  GCancellableSource *cancellable_source;
+
+  source = g_source_new (&cancellable_source_funcs, sizeof (GCancellableSource));
+  g_source_set_name (source, "GCancellable");
+  cancellable_source = (GCancellableSource *)source;
+
+  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_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;
+}