|
|
|
@@ -108,6 +108,7 @@ enum {
|
|
|
|
|
#define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1) /* Set if object ever had a signal handler */
|
|
|
|
|
#define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2) /* Same, specifically for "notify" */
|
|
|
|
|
#define OPTIONAL_FLAG_LOCK (1 << 3) /* _OPTIONAL_BIT_LOCK */
|
|
|
|
|
#define OPTIONAL_FLAG_EVER_HAD_WEAK_REF (1 << 4) /* whether on the object ever g_weak_ref_set() was called. */
|
|
|
|
|
|
|
|
|
|
/* We use g_bit_lock(), which only supports one lock per integer.
|
|
|
|
|
*
|
|
|
|
@@ -206,10 +207,11 @@ static void g_object_dispatch_properties_changed (GObject *object,
|
|
|
|
|
GParamSpec **pspecs);
|
|
|
|
|
static guint object_floating_flag_handler (GObject *object,
|
|
|
|
|
gint job);
|
|
|
|
|
static inline void object_set_optional_flags (GObject *object,
|
|
|
|
|
guint flags);
|
|
|
|
|
|
|
|
|
|
static void object_interface_check_properties (gpointer check_data,
|
|
|
|
|
gpointer g_iface);
|
|
|
|
|
static void weak_locations_free_unlocked (GSList **weak_locations);
|
|
|
|
|
|
|
|
|
|
/* --- typedefs --- */
|
|
|
|
|
typedef struct _GObjectNotifyQueue GObjectNotifyQueue;
|
|
|
|
@@ -229,9 +231,7 @@ static GQuark quark_notify_queue;
|
|
|
|
|
static GParamSpecPool *pspec_pool = NULL;
|
|
|
|
|
static gulong gobject_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
|
static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler;
|
|
|
|
|
/* qdata pointing to GSList<GWeakRef *>, protected by weak_locations_lock */
|
|
|
|
|
static GQuark quark_weak_locations = 0;
|
|
|
|
|
static GRWLock weak_locations_lock;
|
|
|
|
|
|
|
|
|
|
#if HAVE_PRIVATE
|
|
|
|
|
G_ALWAYS_INLINE static inline GObjectPrivate *
|
|
|
|
@@ -251,6 +251,209 @@ object_get_optional_flags_p (GObject *object)
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/* For GWeakRef, we need to take a lock per-object. However, in various cases
|
|
|
|
|
* we cannot take a strong reference on the object to keep it alive. So the
|
|
|
|
|
* mutex cannot be in the object itself, because when we want to release the
|
|
|
|
|
* lock, we can no longer access object.
|
|
|
|
|
*
|
|
|
|
|
* Instead, the mutex is on the WeakRefData, which is itself ref-counted
|
|
|
|
|
* and has a separate lifetime from the object. */
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
gatomicrefcount ref_count;
|
|
|
|
|
gint lock_flags; /* (atomic) */
|
|
|
|
|
GSList *list; /* (element-type GWeakRef) (owned) */
|
|
|
|
|
} WeakRefData;
|
|
|
|
|
|
|
|
|
|
static void weak_ref_data_clear_list (WeakRefData *wrdata, GObject *object);
|
|
|
|
|
|
|
|
|
|
static WeakRefData *
|
|
|
|
|
weak_ref_data_ref (WeakRefData *wrdata)
|
|
|
|
|
{
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (wrdata);
|
|
|
|
|
#endif
|
|
|
|
|
g_atomic_ref_count_inc (&wrdata->ref_count);
|
|
|
|
|
return wrdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
weak_ref_data_unref (WeakRefData *wrdata)
|
|
|
|
|
{
|
|
|
|
|
if (!wrdata)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!g_atomic_ref_count_dec (&wrdata->ref_count))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
/* We expect that the list of weak locations is empty at this point.
|
|
|
|
|
* During g_object_unref() (_object_unref_clear_weak_locations()) it
|
|
|
|
|
* should have been cleared.
|
|
|
|
|
*
|
|
|
|
|
* Calling weak_ref_data_clear_list() should be unnecessary. */
|
|
|
|
|
g_assert (!wrdata->list);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
g_free_sized (wrdata, sizeof (WeakRefData));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
weak_ref_data_lock (WeakRefData *wrdata)
|
|
|
|
|
{
|
|
|
|
|
/* Note that while holding a _weak_ref_lock() on the @weak_ref, we MUST not acquire a
|
|
|
|
|
* weak_ref_data_lock() on the @wrdata. The other way around! */
|
|
|
|
|
if (wrdata)
|
|
|
|
|
g_bit_lock (&wrdata->lock_flags, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
weak_ref_data_unlock (WeakRefData *wrdata)
|
|
|
|
|
{
|
|
|
|
|
if (wrdata)
|
|
|
|
|
g_bit_unlock (&wrdata->lock_flags, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gpointer
|
|
|
|
|
weak_ref_data_get_or_create_cb (GQuark key_id,
|
|
|
|
|
gpointer *data,
|
|
|
|
|
GDestroyNotify *destroy_notify,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
WeakRefData *wrdata = *data;
|
|
|
|
|
GObject *object = user_data;
|
|
|
|
|
|
|
|
|
|
if (!wrdata)
|
|
|
|
|
{
|
|
|
|
|
wrdata = g_new (WeakRefData, 1);
|
|
|
|
|
*wrdata = (WeakRefData){
|
|
|
|
|
/* The initial ref-count is 1. This one is owned by the GData until the
|
|
|
|
|
* object gets destroyed. */
|
|
|
|
|
.ref_count = 1,
|
|
|
|
|
.lock_flags = 0,
|
|
|
|
|
.list = NULL,
|
|
|
|
|
};
|
|
|
|
|
*data = wrdata;
|
|
|
|
|
*destroy_notify = (GDestroyNotify) weak_ref_data_unref;
|
|
|
|
|
|
|
|
|
|
/* Mark the @object that it was ever involved with GWeakRef. This flag
|
|
|
|
|
* will stick until @object gets destroyed, just like the WeakRefData
|
|
|
|
|
* also won't be freed for the remainder of the life of @object. */
|
|
|
|
|
object_set_optional_flags (object, OPTIONAL_FLAG_EVER_HAD_WEAK_REF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wrdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WeakRefData *
|
|
|
|
|
weak_ref_data_get_or_create (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
if (!object)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return _g_datalist_id_update_atomic (&object->qdata,
|
|
|
|
|
quark_weak_locations,
|
|
|
|
|
weak_ref_data_get_or_create_cb,
|
|
|
|
|
object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WeakRefData *
|
|
|
|
|
weak_ref_data_get (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
return g_datalist_id_get_data (&object->qdata, quark_weak_locations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WeakRefData *
|
|
|
|
|
weak_ref_data_get_surely (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
WeakRefData *wrdata;
|
|
|
|
|
|
|
|
|
|
/* The "surely" part is about that we expect to have a WeakRefData.
|
|
|
|
|
*
|
|
|
|
|
* Note that once a GObject gets a WeakRefData (during g_weak_ref_set() and
|
|
|
|
|
* weak_ref_data_get_or_create()), it sticks and is not freed until the
|
|
|
|
|
* object gets destroyed.
|
|
|
|
|
*
|
|
|
|
|
* Maybe we could release the unused WeakRefData in g_weak_ref_set(), but
|
|
|
|
|
* then we would always need to take a reference during weak_ref_data_get().
|
|
|
|
|
* That is likely not worth it. */
|
|
|
|
|
|
|
|
|
|
wrdata = weak_ref_data_get (object);
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (wrdata);
|
|
|
|
|
#endif
|
|
|
|
|
return wrdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
weak_ref_data_has (GObject *object, WeakRefData *wrdata, WeakRefData **out_new_wrdata)
|
|
|
|
|
{
|
|
|
|
|
WeakRefData *wrdata2;
|
|
|
|
|
|
|
|
|
|
/* Check whether @object has @wrdata as WeakRefData. Note that an GObject's
|
|
|
|
|
* WeakRefData never changes (until destruction, once it's allocated).
|
|
|
|
|
*
|
|
|
|
|
* If you thus hold a reference to a @wrdata, you can check that the @object
|
|
|
|
|
* is still the same as the object where we got the @wrdata originally from.
|
|
|
|
|
*
|
|
|
|
|
* You couldn't do this check by using pointer equality of the GObject pointers,
|
|
|
|
|
* when you cannot hold strong references on the objects involved. Because then
|
|
|
|
|
* the object pointer might be dangling (and even destroyed and recreated as another
|
|
|
|
|
* object at the same memory location).
|
|
|
|
|
*
|
|
|
|
|
* Basically, weak_ref_data_has() is to compare for equality of two GObject pointers,
|
|
|
|
|
* when we cannot hold a strong reference on both. Instead, we earlier took a reference
|
|
|
|
|
* on the @wrdata and compare that instead.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!object)
|
|
|
|
|
{
|
|
|
|
|
/* If @object is NULL, then it does have a NULL @wrdata, and we return
|
|
|
|
|
* TRUE in the case. That's a convenient special case for some callers.
|
|
|
|
|
*
|
|
|
|
|
* In other words, weak_ref_data_has(NULL, NULL, out_new_wrdata) is TRUE.
|
|
|
|
|
*/
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (!out_new_wrdata);
|
|
|
|
|
#endif
|
|
|
|
|
return !wrdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!wrdata)
|
|
|
|
|
{
|
|
|
|
|
/* We only call this function with an @object that was previously
|
|
|
|
|
* registered as GWeakRef.
|
|
|
|
|
*
|
|
|
|
|
* That means, our @object will have a wrdata, and the result of the
|
|
|
|
|
* evaluation will be %FALSE. */
|
|
|
|
|
if (out_new_wrdata)
|
|
|
|
|
*out_new_wrdata = weak_ref_data_ref (weak_ref_data_get (object));
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (out_new_wrdata
|
|
|
|
|
? *out_new_wrdata
|
|
|
|
|
: weak_ref_data_get (object));
|
|
|
|
|
#endif
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wrdata2 = weak_ref_data_get_surely (object);
|
|
|
|
|
|
|
|
|
|
if (wrdata == wrdata2)
|
|
|
|
|
{
|
|
|
|
|
if (out_new_wrdata)
|
|
|
|
|
*out_new_wrdata = NULL;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (out_new_wrdata)
|
|
|
|
|
*out_new_wrdata = weak_ref_data_ref (wrdata2);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#if defined(G_ENABLE_DEBUG) && defined(G_THREAD_LOCAL)
|
|
|
|
|
/* Using this thread-local global is sufficient to guard the per-object
|
|
|
|
|
* locking, because while the current thread holds a lock on one object, it
|
|
|
|
@@ -1469,14 +1672,25 @@ g_object_dispatch_properties_changed (GObject *object,
|
|
|
|
|
void
|
|
|
|
|
g_object_run_dispose (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
WeakRefData *wrdata;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
|
g_return_if_fail (g_atomic_int_get (&object->ref_count) > 0);
|
|
|
|
|
|
|
|
|
|
g_object_ref (object);
|
|
|
|
|
|
|
|
|
|
TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 0));
|
|
|
|
|
G_OBJECT_GET_CLASS (object)->dispose (object);
|
|
|
|
|
TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 0));
|
|
|
|
|
g_datalist_id_remove_data (&object->qdata, quark_weak_locations);
|
|
|
|
|
|
|
|
|
|
if ((object_get_optional_flags (object) & OPTIONAL_FLAG_EVER_HAD_WEAK_REF))
|
|
|
|
|
{
|
|
|
|
|
wrdata = weak_ref_data_get_surely (object);
|
|
|
|
|
weak_ref_data_lock (wrdata);
|
|
|
|
|
weak_ref_data_clear_list (wrdata, object);
|
|
|
|
|
weak_ref_data_unlock (wrdata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_object_unref (object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3839,77 +4053,56 @@ gpointer
|
|
|
|
|
static gboolean
|
|
|
|
|
_object_unref_clear_weak_locations (GObject *object, gint *p_old_ref, gboolean do_unref)
|
|
|
|
|
{
|
|
|
|
|
GSList **weak_locations;
|
|
|
|
|
WeakRefData *wrdata;
|
|
|
|
|
gboolean success;
|
|
|
|
|
|
|
|
|
|
/* Fast path, for objects that never had a GWeakRef registered. */
|
|
|
|
|
if (!(object_get_optional_flags (object) & OPTIONAL_FLAG_EVER_HAD_WEAK_REF))
|
|
|
|
|
{
|
|
|
|
|
/* The caller previously just checked atomically that the ref-count was
|
|
|
|
|
* one.
|
|
|
|
|
*
|
|
|
|
|
* At this point still, @object never ever had a GWeakRef registered.
|
|
|
|
|
* That means, nobody else holds a strong reference and also nobody else
|
|
|
|
|
* can hold a weak reference, to race against obtaining another
|
|
|
|
|
* reference. We are good to proceed. */
|
|
|
|
|
if (do_unref)
|
|
|
|
|
{
|
|
|
|
|
if (!g_atomic_int_compare_and_exchange ((gint *) &object->ref_count, 1, 0))
|
|
|
|
|
{
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Slow path. We must obtain a lock on the @wrdata, to atomically release
|
|
|
|
|
* weak references and check that the ref count is as expected. */
|
|
|
|
|
|
|
|
|
|
wrdata = weak_ref_data_get_surely (object);
|
|
|
|
|
|
|
|
|
|
weak_ref_data_lock (wrdata);
|
|
|
|
|
|
|
|
|
|
if (do_unref)
|
|
|
|
|
{
|
|
|
|
|
gboolean unreffed = FALSE;
|
|
|
|
|
|
|
|
|
|
/* Fast path for the final unref using a read-lck only. We check whether
|
|
|
|
|
* we have weak_locations and drop ref count to zero under a reader lock. */
|
|
|
|
|
|
|
|
|
|
g_rw_lock_reader_lock (&weak_locations_lock);
|
|
|
|
|
|
|
|
|
|
weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
|
|
|
|
|
if (!weak_locations)
|
|
|
|
|
{
|
|
|
|
|
unreffed = g_atomic_int_compare_and_exchange_full ((int *) &object->ref_count,
|
|
|
|
|
1, 0,
|
|
|
|
|
p_old_ref);
|
|
|
|
|
g_rw_lock_reader_unlock (&weak_locations_lock);
|
|
|
|
|
return unreffed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_rw_lock_reader_unlock (&weak_locations_lock);
|
|
|
|
|
|
|
|
|
|
/* We have weak-locations. Note that we are here already after dispose(). That
|
|
|
|
|
* means, during dispose a GWeakRef was registered (very unusual). */
|
|
|
|
|
|
|
|
|
|
g_rw_lock_writer_lock (&weak_locations_lock);
|
|
|
|
|
|
|
|
|
|
if (!g_atomic_int_compare_and_exchange_full ((int *) &object->ref_count,
|
|
|
|
|
1, 0,
|
|
|
|
|
p_old_ref))
|
|
|
|
|
{
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weak_locations = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_locations);
|
|
|
|
|
g_clear_pointer (&weak_locations, weak_locations_free_unlocked);
|
|
|
|
|
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
return TRUE;
|
|
|
|
|
success = g_atomic_int_compare_and_exchange_full ((gint *) &object->ref_count,
|
|
|
|
|
1, 0,
|
|
|
|
|
p_old_ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
|
|
|
|
|
if (weak_locations != NULL)
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_rw_lock_writer_lock (&weak_locations_lock);
|
|
|
|
|
|
|
|
|
|
*p_old_ref = g_atomic_int_get (&object->ref_count);
|
|
|
|
|
if (*p_old_ref != 1)
|
|
|
|
|
{
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weak_locations = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_locations);
|
|
|
|
|
g_clear_pointer (&weak_locations, weak_locations_free_unlocked);
|
|
|
|
|
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
return TRUE;
|
|
|
|
|
*p_old_ref = g_atomic_int_get ((gint *) &object->ref_count);
|
|
|
|
|
success = (*p_old_ref == 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We don't need to re-fetch p_old_ref or check that it's still 1. The caller
|
|
|
|
|
* did that already. We are good.
|
|
|
|
|
*
|
|
|
|
|
* Note that in this case we fetched old_ref and weak_locations separately,
|
|
|
|
|
* without a lock. But this is fine. We are still before calling dispose().
|
|
|
|
|
* If there is a race at this point, the same race can happen between
|
|
|
|
|
* _object_unref_clear_weak_locations() and dispose() call. That is handled
|
|
|
|
|
* just fine. */
|
|
|
|
|
return TRUE;
|
|
|
|
|
if (success)
|
|
|
|
|
weak_ref_data_clear_list (wrdata, object);
|
|
|
|
|
|
|
|
|
|
weak_ref_data_unlock (wrdata);
|
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -4032,6 +4225,10 @@ retry_beginning:
|
|
|
|
|
G_OBJECT_GET_CLASS (object)->dispose (object);
|
|
|
|
|
TRACE (GOBJECT_OBJECT_DISPOSE_END (object, G_TYPE_FROM_INSTANCE (object), 1));
|
|
|
|
|
|
|
|
|
|
/* Must re-fetch old-ref. _object_unref_clear_weak_locations() relies on
|
|
|
|
|
* that. */
|
|
|
|
|
old_ref = g_atomic_int_get (&object->ref_count);
|
|
|
|
|
|
|
|
|
|
retry_decrement:
|
|
|
|
|
/* Here, old_ref is 1 if we just come from dispose(). If the object was resurrected,
|
|
|
|
|
* we can hit `goto retry_decrement` and be here with a larger old_ref. */
|
|
|
|
@@ -4042,6 +4239,15 @@ retry_decrement:
|
|
|
|
|
* queue. */
|
|
|
|
|
g_object_notify_queue_thaw (object, nqueue, FALSE);
|
|
|
|
|
nqueue = NULL;
|
|
|
|
|
|
|
|
|
|
/* Note at this point, @old_ref might be wrong.
|
|
|
|
|
*
|
|
|
|
|
* Also note that _object_unref_clear_weak_locations() requires that we
|
|
|
|
|
* atomically checked that @old_ref is 1. However, as @old_ref is larger
|
|
|
|
|
* than 1, that will not be called. Instead, all other code paths below,
|
|
|
|
|
* handle the possibility of a bogus @old_ref.
|
|
|
|
|
*
|
|
|
|
|
* No need to re-fetch. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (old_ref > 2)
|
|
|
|
@@ -4080,9 +4286,8 @@ retry_decrement:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* old_ref is 1, we are about to drop the reference count to zero. That is
|
|
|
|
|
* done by _object_unref_clear_weak_locations() under a weak_locations_lock
|
|
|
|
|
* so that there is no race with g_weak_ref_set(). */
|
|
|
|
|
/* old_ref is (atomically!) checked to be 1, we are about to drop the
|
|
|
|
|
* reference count to zero in _object_unref_clear_weak_locations(). */
|
|
|
|
|
if (!_object_unref_clear_weak_locations (object, &old_ref, TRUE))
|
|
|
|
|
goto retry_decrement;
|
|
|
|
|
|
|
|
|
@@ -5044,6 +5249,201 @@ g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
|
|
|
|
|
* without first having or creating a strong reference to the object.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define WEAK_REF_LOCK_BIT 0
|
|
|
|
|
|
|
|
|
|
static GObject *
|
|
|
|
|
_weak_ref_clean_pointer (gpointer ptr)
|
|
|
|
|
{
|
|
|
|
|
/* Drop the lockbit WEAK_REF_LOCK_BIT from @ptr (if set). */
|
|
|
|
|
return g_pointer_bit_lock_mask_ptr (ptr, WEAK_REF_LOCK_BIT, FALSE, 0, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_weak_ref_lock (GWeakRef *weak_ref, GObject **out_object)
|
|
|
|
|
{
|
|
|
|
|
/* Note that while holding a _weak_ref_lock() on the @weak_ref, we MUST not acquire a
|
|
|
|
|
* weak_ref_data_lock() on the @wrdata. The other way around! */
|
|
|
|
|
|
|
|
|
|
if (out_object)
|
|
|
|
|
{
|
|
|
|
|
guintptr ptr;
|
|
|
|
|
|
|
|
|
|
g_pointer_bit_lock_and_get (&weak_ref->priv.p, WEAK_REF_LOCK_BIT, &ptr);
|
|
|
|
|
*out_object = _weak_ref_clean_pointer ((gpointer) ptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_pointer_bit_lock (&weak_ref->priv.p, WEAK_REF_LOCK_BIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_weak_ref_unlock (GWeakRef *weak_ref)
|
|
|
|
|
{
|
|
|
|
|
g_pointer_bit_unlock (&weak_ref->priv.p, WEAK_REF_LOCK_BIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_weak_ref_unlock_and_set (GWeakRef *weak_ref, GObject *object)
|
|
|
|
|
{
|
|
|
|
|
g_pointer_bit_unlock_and_set (&weak_ref->priv.p, WEAK_REF_LOCK_BIT, object, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
weak_ref_data_clear_list (WeakRefData *wrdata, GObject *object)
|
|
|
|
|
{
|
|
|
|
|
while (wrdata->list)
|
|
|
|
|
{
|
|
|
|
|
GWeakRef *weak_ref = wrdata->list->data;
|
|
|
|
|
gpointer ptr;
|
|
|
|
|
|
|
|
|
|
wrdata->list = g_slist_remove (wrdata->list, weak_ref);
|
|
|
|
|
|
|
|
|
|
/* Fast-path. Most likely @weak_ref is currently not locked, so we can
|
|
|
|
|
* just atomically set the pointer to NULL. */
|
|
|
|
|
ptr = g_atomic_pointer_get (&weak_ref->priv.p);
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (G_IS_OBJECT (_weak_ref_clean_pointer (ptr)));
|
|
|
|
|
g_assert (!object || object == _weak_ref_clean_pointer (ptr));
|
|
|
|
|
#endif
|
|
|
|
|
if (G_LIKELY (ptr == _weak_ref_clean_pointer (ptr)))
|
|
|
|
|
{
|
|
|
|
|
/* The pointer is unlocked. Try an atomic compare-and-exchange... */
|
|
|
|
|
if (g_atomic_pointer_compare_and_exchange (&weak_ref->priv.p, ptr, NULL))
|
|
|
|
|
{
|
|
|
|
|
/* Done. Go to the next. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The @weak_ref is locked. Acquire the lock to set the pointer to NULL. */
|
|
|
|
|
_weak_ref_lock (weak_ref, NULL);
|
|
|
|
|
_weak_ref_unlock_and_set (weak_ref, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_weak_ref_set (GWeakRef *weak_ref,
|
|
|
|
|
GObject *new_object,
|
|
|
|
|
gboolean called_by_init)
|
|
|
|
|
{
|
|
|
|
|
WeakRefData *old_wrdata;
|
|
|
|
|
WeakRefData *new_wrdata;
|
|
|
|
|
GObject *old_object;
|
|
|
|
|
|
|
|
|
|
new_wrdata = weak_ref_data_get_or_create (new_object);
|
|
|
|
|
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (!new_object || object_get_optional_flags (new_object) & OPTIONAL_FLAG_EVER_HAD_WEAK_REF);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (called_by_init)
|
|
|
|
|
{
|
|
|
|
|
/* The caller is g_weak_ref_init(). We know that the weak_ref should be
|
|
|
|
|
* NULL. We thus set @old_wrdata to NULL without checking.
|
|
|
|
|
*
|
|
|
|
|
* Also important, the caller ensured that @new_object is not NULL. So we
|
|
|
|
|
* are expected to set @weak_ref from NULL to a non-NULL @new_object. */
|
|
|
|
|
old_wrdata = NULL;
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (new_object);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* We must get a wrdata object @old_wrdata for the current @old_object. */
|
|
|
|
|
_weak_ref_lock (weak_ref, &old_object);
|
|
|
|
|
|
|
|
|
|
if (old_object == new_object)
|
|
|
|
|
{
|
|
|
|
|
/* Already set. We are done. */
|
|
|
|
|
_weak_ref_unlock (weak_ref);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old_wrdata = old_object
|
|
|
|
|
? weak_ref_data_ref (weak_ref_data_get (old_object))
|
|
|
|
|
: NULL;
|
|
|
|
|
_weak_ref_unlock (weak_ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We need a lock on @old_wrdata, @new_wrdata and @weak_ref. We need to take
|
|
|
|
|
* these locks in a certain order to avoid deadlock. We sort them by pointer
|
|
|
|
|
* value.
|
|
|
|
|
*
|
|
|
|
|
* Note that @old_wrdata or @new_wrdata may be NULL, which is handled
|
|
|
|
|
* correctly.
|
|
|
|
|
*
|
|
|
|
|
* Note that @old_wrdata and @new_wrdata are never identical at this point.
|
|
|
|
|
*/
|
|
|
|
|
if (new_wrdata && old_wrdata && (((guintptr) (gpointer) old_wrdata) < ((guintptr) ((gpointer) new_wrdata))))
|
|
|
|
|
{
|
|
|
|
|
weak_ref_data_lock (old_wrdata);
|
|
|
|
|
weak_ref_data_lock (new_wrdata);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
weak_ref_data_lock (new_wrdata);
|
|
|
|
|
weak_ref_data_lock (old_wrdata);
|
|
|
|
|
}
|
|
|
|
|
_weak_ref_lock (weak_ref, &old_object);
|
|
|
|
|
|
|
|
|
|
if (!weak_ref_data_has (old_object, old_wrdata, NULL))
|
|
|
|
|
{
|
|
|
|
|
/* A race. @old_object no longer has the expected @old_wrdata after
|
|
|
|
|
* getting all the locks. */
|
|
|
|
|
if (old_object)
|
|
|
|
|
{
|
|
|
|
|
/* We lost the race and find a different object set. It's fine, our
|
|
|
|
|
* action was lost in the race and we are done. No need to retry. */
|
|
|
|
|
weak_ref_data_unlock (old_wrdata);
|
|
|
|
|
weak_ref_data_unlock (new_wrdata);
|
|
|
|
|
_weak_ref_unlock (weak_ref);
|
|
|
|
|
weak_ref_data_unref (old_wrdata);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* @old_object is NULL after a race. We didn't expect that, but it's
|
|
|
|
|
* fine. Proceed to set @new_object... */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (old_object)
|
|
|
|
|
{
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (g_slist_find (old_wrdata->list, weak_ref));
|
|
|
|
|
#endif
|
|
|
|
|
if (!old_wrdata->list)
|
|
|
|
|
{
|
|
|
|
|
g_critical ("unexpected missing GWeakRef data");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
old_wrdata->list = g_slist_remove (old_wrdata->list, weak_ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weak_ref_data_unlock (old_wrdata);
|
|
|
|
|
|
|
|
|
|
if (new_object)
|
|
|
|
|
{
|
|
|
|
|
#if G_ENABLE_DEBUG
|
|
|
|
|
g_assert (!g_slist_find (new_wrdata->list, weak_ref));
|
|
|
|
|
#endif
|
|
|
|
|
if (g_atomic_int_get (&new_object->ref_count) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_critical ("calling g_weak_ref_set() with already destroyed object");
|
|
|
|
|
new_object = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
new_wrdata->list = g_slist_prepend (new_wrdata->list, weak_ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_weak_ref_unlock_and_set (weak_ref, new_object);
|
|
|
|
|
weak_ref_data_unlock (new_wrdata);
|
|
|
|
|
|
|
|
|
|
weak_ref_data_unref (old_wrdata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_weak_ref_init: (skip)
|
|
|
|
|
* @weak_ref: (inout): uninitialized or empty location for a weak
|
|
|
|
@@ -5064,12 +5464,19 @@ g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
g_weak_ref_init (GWeakRef *weak_ref,
|
|
|
|
|
gpointer object)
|
|
|
|
|
gpointer object)
|
|
|
|
|
{
|
|
|
|
|
weak_ref->priv.p = NULL;
|
|
|
|
|
g_return_if_fail (weak_ref);
|
|
|
|
|
g_return_if_fail (object == NULL || G_IS_OBJECT (object));
|
|
|
|
|
|
|
|
|
|
g_atomic_pointer_set (&weak_ref->priv.p, NULL);
|
|
|
|
|
if (object)
|
|
|
|
|
g_weak_ref_set (weak_ref, object);
|
|
|
|
|
{
|
|
|
|
|
/* We give a hint that the weak_ref is currently NULL. Unlike
|
|
|
|
|
* g_weak_ref_set(), we then don't need the extra lock just to
|
|
|
|
|
* find out that we have no object. */
|
|
|
|
|
_weak_ref_set (weak_ref, object, TRUE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -5116,20 +5523,86 @@ g_weak_ref_clear (GWeakRef *weak_ref)
|
|
|
|
|
gpointer
|
|
|
|
|
g_weak_ref_get (GWeakRef *weak_ref)
|
|
|
|
|
{
|
|
|
|
|
WeakRefData *wrdata;
|
|
|
|
|
WeakRefData *new_wrdata;
|
|
|
|
|
GToggleNotify toggle_notify = NULL;
|
|
|
|
|
gpointer toggle_data = NULL;
|
|
|
|
|
GObject *object;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (weak_ref, NULL);
|
|
|
|
|
|
|
|
|
|
g_rw_lock_reader_lock (&weak_locations_lock);
|
|
|
|
|
/* We cannot take the strong reference on @object yet. Otherwise,
|
|
|
|
|
* _object_unref_clear_weak_locations() might have just taken the lock on
|
|
|
|
|
* @wrdata, see that the ref-count is 1 and plan to proceed clearing weak
|
|
|
|
|
* locations. If we then take a strong reference here, the object becomes
|
|
|
|
|
* alive and well, but _object_unref_clear_weak_locations() would proceed and
|
|
|
|
|
* clear the @weak_ref.
|
|
|
|
|
*
|
|
|
|
|
* We avoid that, by can only taking the strong reference when having a lock
|
|
|
|
|
* on @wrdata, so we are in sync with _object_unref_clear_weak_locations().
|
|
|
|
|
*
|
|
|
|
|
* But first we must get a reference to the @wrdata.
|
|
|
|
|
*/
|
|
|
|
|
_weak_ref_lock (weak_ref, &object);
|
|
|
|
|
wrdata = object
|
|
|
|
|
? weak_ref_data_ref (weak_ref_data_get (object))
|
|
|
|
|
: NULL;
|
|
|
|
|
_weak_ref_unlock (weak_ref);
|
|
|
|
|
|
|
|
|
|
object = weak_ref->priv.p;
|
|
|
|
|
if (!wrdata)
|
|
|
|
|
{
|
|
|
|
|
/* There is no @wrdata and no object. We are done. */
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (object)
|
|
|
|
|
object = object_ref (object, &toggle_notify, &toggle_data);
|
|
|
|
|
retry:
|
|
|
|
|
|
|
|
|
|
g_rw_lock_reader_unlock (&weak_locations_lock);
|
|
|
|
|
/* Now proceed to get the strong reference. This time with acquiring a lock
|
|
|
|
|
* on the per-object @wrdata and on @weak_ref.
|
|
|
|
|
*
|
|
|
|
|
* As the order in which locks are taken is important, we previously had to
|
|
|
|
|
* get a _weak_ref_lock(), to obtain the @wrdata. Now we have to lock on the
|
|
|
|
|
* @wrdata first, and the @weak_ref again. */
|
|
|
|
|
weak_ref_data_lock (wrdata);
|
|
|
|
|
_weak_ref_lock (weak_ref, &object);
|
|
|
|
|
|
|
|
|
|
if (!object)
|
|
|
|
|
{
|
|
|
|
|
/* Object is gone in the meantime. That is fine. */
|
|
|
|
|
new_wrdata = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Check that @object still refers to the same object as before. We do
|
|
|
|
|
* that by comparing the @wrdata object. A GObject keeps its (unique!)
|
|
|
|
|
* wrdata instance until the end, and since @wrdata is still alive,
|
|
|
|
|
* @object is the same as before, if-and-only-if its @wrdata is the same.
|
|
|
|
|
*/
|
|
|
|
|
if (weak_ref_data_has (object, wrdata, &new_wrdata))
|
|
|
|
|
{
|
|
|
|
|
/* We are (still) good. Take a strong ref while holding the necessary locks. */
|
|
|
|
|
object = object_ref (object, &toggle_notify, &toggle_data);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* The @object changed and has no longer the same @wrdata. In this
|
|
|
|
|
* case, we need to start over.
|
|
|
|
|
*
|
|
|
|
|
* Note that @new_wrdata references the wrdata of the now current
|
|
|
|
|
* @object. We will use that during the retry. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_weak_ref_unlock (weak_ref);
|
|
|
|
|
weak_ref_data_unlock (wrdata);
|
|
|
|
|
weak_ref_data_unref (wrdata);
|
|
|
|
|
|
|
|
|
|
if (new_wrdata)
|
|
|
|
|
{
|
|
|
|
|
/* There was a race. The object changed. Retry, with @new_wrdata. */
|
|
|
|
|
wrdata = new_wrdata;
|
|
|
|
|
goto retry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (toggle_notify)
|
|
|
|
|
toggle_notify (toggle_data, object, FALSE);
|
|
|
|
@@ -5137,35 +5610,6 @@ g_weak_ref_get (GWeakRef *weak_ref)
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
weak_locations_free_unlocked (GSList **weak_locations)
|
|
|
|
|
{
|
|
|
|
|
if (*weak_locations)
|
|
|
|
|
{
|
|
|
|
|
GSList *weak_location;
|
|
|
|
|
|
|
|
|
|
for (weak_location = *weak_locations; weak_location;)
|
|
|
|
|
{
|
|
|
|
|
GWeakRef *weak_ref_location = weak_location->data;
|
|
|
|
|
|
|
|
|
|
weak_ref_location->priv.p = NULL;
|
|
|
|
|
weak_location = g_slist_delete_link (weak_location, weak_location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (weak_locations);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
weak_locations_free (gpointer data)
|
|
|
|
|
{
|
|
|
|
|
GSList **weak_locations = data;
|
|
|
|
|
|
|
|
|
|
g_rw_lock_writer_lock (&weak_locations_lock);
|
|
|
|
|
weak_locations_free_unlocked (weak_locations);
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* g_weak_ref_set: (skip)
|
|
|
|
|
* @weak_ref: location for a weak reference
|
|
|
|
@@ -5181,82 +5625,10 @@ weak_locations_free (gpointer data)
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
g_weak_ref_set (GWeakRef *weak_ref,
|
|
|
|
|
gpointer object)
|
|
|
|
|
gpointer object)
|
|
|
|
|
{
|
|
|
|
|
GSList **weak_locations;
|
|
|
|
|
GObject *new_object;
|
|
|
|
|
GObject *old_object;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (weak_ref != NULL);
|
|
|
|
|
g_return_if_fail (object == NULL || G_IS_OBJECT (object));
|
|
|
|
|
|
|
|
|
|
new_object = object;
|
|
|
|
|
|
|
|
|
|
g_rw_lock_writer_lock (&weak_locations_lock);
|
|
|
|
|
|
|
|
|
|
/* We use the extra level of indirection here so that if we have ever
|
|
|
|
|
* had a weak pointer installed at any point in time on this object,
|
|
|
|
|
* we can see that there is a non-NULL value associated with the
|
|
|
|
|
* weak-pointer quark and know that this value will not change at any
|
|
|
|
|
* point in the object's lifetime.
|
|
|
|
|
*
|
|
|
|
|
* Both properties are important for reducing the amount of times we
|
|
|
|
|
* need to acquire locks and for decreasing the duration of time the
|
|
|
|
|
* lock is held while avoiding some rather tricky races.
|
|
|
|
|
*
|
|
|
|
|
* Specifically: we can avoid having to do an extra unconditional lock
|
|
|
|
|
* in g_object_unref() without worrying about some extremely tricky
|
|
|
|
|
* races.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
old_object = weak_ref->priv.p;
|
|
|
|
|
if (new_object != old_object)
|
|
|
|
|
{
|
|
|
|
|
weak_ref->priv.p = new_object;
|
|
|
|
|
|
|
|
|
|
/* Remove the weak ref from the old object */
|
|
|
|
|
if (old_object != NULL)
|
|
|
|
|
{
|
|
|
|
|
weak_locations = g_datalist_id_get_data (&old_object->qdata, quark_weak_locations);
|
|
|
|
|
if (weak_locations == NULL)
|
|
|
|
|
{
|
|
|
|
|
g_critical ("unexpected missing GWeakRef");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*weak_locations = g_slist_remove (*weak_locations, weak_ref);
|
|
|
|
|
|
|
|
|
|
if (!*weak_locations)
|
|
|
|
|
{
|
|
|
|
|
weak_locations_free_unlocked (weak_locations);
|
|
|
|
|
g_datalist_id_remove_no_notify (&old_object->qdata, quark_weak_locations);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the weak ref to the new object */
|
|
|
|
|
if (new_object != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (g_atomic_int_get (&new_object->ref_count) < 1)
|
|
|
|
|
{
|
|
|
|
|
weak_ref->priv.p = NULL;
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
g_critical ("calling g_weak_ref_set() with already destroyed object");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weak_locations = g_datalist_id_get_data (&new_object->qdata, quark_weak_locations);
|
|
|
|
|
|
|
|
|
|
if (weak_locations == NULL)
|
|
|
|
|
{
|
|
|
|
|
weak_locations = g_new0 (GSList *, 1);
|
|
|
|
|
g_datalist_id_set_data_full (&new_object->qdata, quark_weak_locations,
|
|
|
|
|
weak_locations, weak_locations_free);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*weak_locations = g_slist_prepend (*weak_locations, weak_ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_rw_lock_writer_unlock (&weak_locations_lock);
|
|
|
|
|
_weak_ref_set (weak_ref, object, FALSE);
|
|
|
|
|
}
|
|
|
|
|