mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-28 10:30:03 +01:00
gobject: add code comment about unlock and toggle_refs_check_and_ref_or_deref()
It may not be obvious, but the moment unlock is called, the locker instance may be destroyed. See g_object_unref(), which calls toggle_refs_check_and_ref_or_deref(). It will check for toggle references while dropping the ref count from 2 to 1. It must decrement the ref count while holding the lock, but it also must still unlock afterwards. Note that the locker instance is on the object itself. Once we decrement the ref count we give up our reference and another thread may race against destroying the object. We thus must not touch object anymore. How can we then still unlock? This works correctly because: - unlock operations must not touch the locker instance after unlocking. - assume that another thread races g_object_unref() to destroy the object, while we are about to call object_bit_unlock() in toggle_refs_check_and_ref_or_deref(). Then that other thread will also need to acquire the same lock (during g_object_notify_queue_freeze()). It thus is blocked to destroy the object. Add code comments about that.
This commit is contained in:
parent
f17cfcf930
commit
1b298d1db1
@ -342,6 +342,10 @@ g_bit_unlock (volatile gint *address,
|
||||
g_atomic_int_and (address_nonvolatile, ~mask);
|
||||
#endif
|
||||
|
||||
/* Warning: unlocking may allow another thread to proceed and destroy the
|
||||
* memory that @address points to. We thus must not dereference it anymore.
|
||||
*/
|
||||
|
||||
{
|
||||
guint class = bit_lock_contended_class (address_nonvolatile);
|
||||
|
||||
@ -599,6 +603,10 @@ void
|
||||
g_atomic_pointer_and (pointer_address, ~mask);
|
||||
#endif
|
||||
|
||||
/* Warning: unlocking may allow another thread to proceed and destroy the
|
||||
* memory that @address points to. We thus must not dereference it anymore.
|
||||
*/
|
||||
|
||||
{
|
||||
guint class = bit_lock_contended_class (address_nonvolatile);
|
||||
|
||||
|
@ -644,6 +644,9 @@ object_bit_unlock (GObject *object, guint lock_bit)
|
||||
_object_bit_is_locked = 0;
|
||||
#endif
|
||||
|
||||
/* Warning: after unlock, @object may be a dangling pointer (destroyed on
|
||||
* another thread) and must not be touched anymore. */
|
||||
|
||||
g_bit_unlock ((gint *) object_get_optional_flags_p (object), _OPTIONAL_BIT_LOCK);
|
||||
}
|
||||
|
||||
@ -3977,6 +3980,20 @@ toggle_refs_check_and_ref_or_deref (GObject *object,
|
||||
ref_next,
|
||||
old_ref);
|
||||
|
||||
/* Note that if we are called during g_object_unref (@is_ref set to FALSE),
|
||||
* then we drop the ref count from 2 to 1 and give up our reference. We thus
|
||||
* no longer hold a strong reference and another thread may race against
|
||||
* destroying the object.
|
||||
*
|
||||
* After this point with is_ref=FALSE and success=TRUE, @object must no
|
||||
* longer be accessed.
|
||||
*
|
||||
* The exception is here. While we still hold the object lock, we know that
|
||||
* @object could not be destroyed, because g_object_unref() also needs to
|
||||
* acquire the same lock during g_object_notify_queue_freeze(). Thus, we know
|
||||
* object cannot yet be destroyed and we can access it until the unlock
|
||||
* below. */
|
||||
|
||||
if (success && OBJECT_HAS_TOGGLE_REF (object))
|
||||
{
|
||||
ToggleRefStack *tstackptr;
|
||||
@ -4385,6 +4402,10 @@ retry_beginning:
|
||||
* notifications. If the instance gets through to finalize(), the
|
||||
* notification queue gets automatically drained when g_object_finalize() is
|
||||
* reached and the qdata is cleared.
|
||||
*
|
||||
* Important: Note that g_object_notify_queue_freeze() takes a object_bit_lock(),
|
||||
* which happens to be the same lock that is also taken by toggle_refs_check_and_ref(),
|
||||
* that is very important. See also the code comment in toggle_refs_check_and_ref().
|
||||
*/
|
||||
nqueue = g_object_notify_queue_freeze (object);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user