Changeset 05ca0d8 for util.c


Ignore:
Timestamp:
Jun 1, 2010, 3:30:12 AM (11 years ago)
Author:
David Benjamin <davidben@mit.edu>
Branches:
master, release-1.7, release-1.8, release-1.9
Children:
d2a4534
Parents:
7a6e6c7
git-author:
David Benjamin <davidben@mit.edu> (06/01/10 02:41:55)
git-committer:
David Benjamin <davidben@mit.edu> (06/01/10 03:30:12)
Message:
Implement our own owl_signal_connect_object

This is actually more code, but that's because of comments. It'll be
MUCH less error-prone.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • util.c

    r4e33cb2 r05ca0d8  
    77#include <sys/stat.h>
    88#include <sys/types.h>
     9
     10#include <glib-object.h>
    911
    1012void sepbar(const char *in)
     
    787789  return buf;
    788790}
     791
     792typedef struct { /*noproto*/
     793  GObject  *sender;
     794  gulong    signal_id;
     795} SignalData;
     796
     797static void _closure_invalidated(gpointer data, GClosure *closure);
     798
     799/*
     800 * GObject's g_signal_connect_object has a documented bug. This function is
     801 * identical except it does not leak the signal handler.
     802 */
     803gulong owl_signal_connect_object(gpointer sender, const gchar *detailed_signal, GCallback c_handler, gpointer receiver, GConnectFlags connect_flags)
     804{
     805  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (sender), 0);
     806  g_return_val_if_fail (detailed_signal != NULL, 0);
     807  g_return_val_if_fail (c_handler != NULL, 0);
     808
     809  if (receiver) {
     810    SignalData *sdata;
     811    GClosure *closure;
     812    gulong signal_id;
     813
     814    g_return_val_if_fail (G_IS_OBJECT (receiver), 0);
     815
     816    closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, receiver);
     817    signal_id = g_signal_connect_closure (sender, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
     818
     819    /* Register the missing hooks */
     820    sdata = g_slice_new0(SignalData);
     821    sdata->sender = sender;
     822    sdata->signal_id = signal_id;
     823
     824    g_closure_add_invalidate_notifier(closure, sdata, _closure_invalidated);
     825
     826    return signal_id;
     827  } else {
     828    return g_signal_connect_data(sender, detailed_signal, c_handler, NULL, NULL, connect_flags);
     829  }
     830}
     831
     832/*
     833 * There are three ways the signal could come to an end:
     834 *
     835 * 1. The user explicitly disconnects it with the returned signal_id.
     836 *    - In that case, the disconnection unref's the closure, causing it
     837 *      to first be invalidated. The handler's already disconnected, so
     838 *      we have no work to do.
     839 * 2. The sender gets destroyed.
     840 *    - GObject will disconnect each signal which then goes into the above
     841 *      case. Our handler does no work.
     842 * 3. The receiver gets destroyed.
     843 *    - The GClosure was created by g_cclosure_new_object_{,swap} which gets
     844 *      invalidated when the receiver is destroyed. We then follow through case 1
     845 *      again, but *this* time, the handler has not been disconnected. We then
     846 *      clean up ourselves.
     847 *
     848 * We can't actually hook into this process earlier with weakrefs as GObject
     849 * will, on object dispose, first disconnect signals, then invalidate closures,
     850 * and notify weakrefs last.
     851 */
     852static void _closure_invalidated(gpointer data, GClosure *closure)
     853{
     854  SignalData *sdata = data;
     855  if (g_signal_handler_is_connected(sdata->sender, sdata->signal_id)) {
     856    g_signal_handler_disconnect(sdata->sender, sdata->signal_id);
     857  }
     858  g_slice_free(SignalData, sdata);
     859}
     860
Note: See TracChangeset for help on using the changeset viewer.