GWeakRef's aren't cleared again on finalization (and not fully thread-safe)
When some code like this (hackish, but just to explain a possible case), is used, the GWeakRef is not unset (again) when about to finalize.
Now, while this code is probably wrong when used explicitly, it can simulate perfectly a case in which we've multiple threads and one is reffing again while (or before) we're about to dispose it. Like:
- thread 1: g_object_unref @ processing weak locations
- thread 2: g_weak_ref's the thread
In bug #865 (closed) it was discussed about performing this during dispose as well, and this could be an option, while the problem here is that after disposition we don't handle it anymore.
Example using "forced" sync code:
#include <glib.h>
#include <gio/gio.h>
static GWeakRef weak_ref;
void (*default_dispose) (GObject *object);
static void
dispose (GObject* gobj)
{
g_print("Object disposed %p\n", gobj);
/* This is still technically possible and supported in unref code
* if done in ->dispose or if another thread adds a reference while we're
* about to dispose... */
g_object_ref (gobj);
g_object_unref (gobj);
default_dispose(gobj);
}
static void
on_toggle_notify (gpointer data, GObject* gobj, gboolean is_last_ref)
{
if (is_last_ref)
g_weak_ref_set(&weak_ref, gobj);
}
int
main(void)
{
GFile *file = g_file_new_for_path ("/");
/* Hacking this to avoid having a whole subtype, but you get the point... */
default_dispose = G_OBJECT_GET_CLASS(file)->dispose;
G_OBJECT_GET_CLASS(file)->dispose = dispose;
g_object_add_toggle_ref(G_OBJECT(file), on_toggle_notify, NULL);
g_object_unref(file);
g_assert_cmpint(G_OBJECT(file)->ref_count, ==, 1);
g_clear_object(&file);
g_assert(g_weak_ref_get(&weak_ref) == NULL);
return 0;
}
Output:
GLib-GObject-Message: 03:23:25.127: FINALIZING Object is 0x556415cfce00, has weak locations: 0x556415cfe9b0
GLib-GObject-Message: 03:23:25.127: FINALIZING Object is 0x556415cfce00, has weak locations: 0x556415cfe9b0, *weak_locations: 0x556415cd0580
GLib-GObject-Message: 03:23:25.127: weak location 0x556414603020
GLib-GObject-Message: 03:23:25.127: Unsetting weak location 0x556414603020 priv 0x556414603020
Object disposed 0x556415cfce00
GLib-GObject-Message: 03:23:25.127: Reffing 0x556415cfce00
(process:479875): GLib-GObject-CRITICAL **: 03:23:25.127: g_object_ref: assertion 'G_IS_OBJECT (object)' failed
**
ERROR:/home/marco/Dev/Snippets/gobject-reffing-weak-on-dispose.c:43:main: assertion failed: (g_weak_ref_get(&weak_ref) == NULL)
Bail out! ERROR:/home/marco/Dev/Snippets/gobject-reffing-weak-on-dispose.c:43:main: assertion failed: (g_weak_ref_get(&weak_ref) == NULL)
[1] 479875 IOT instruction (core dumped) jhbuild run /tmp/ref-test
Using a thread
#include <glib.h>
#include <gio/gio.h>
static GWeakRef weak_ref;
void (*default_dispose) (GObject *object);
static void
dispose (GObject* gobj)
{
g_print("Object disposed %p\n", gobj);
/* Slow down disposition so we give chance to the thread to act */
g_usleep(1000);
g_print("Completing disposition... %p\n", gobj);
default_dispose(gobj);
}
static gpointer
on_other_thread (gpointer gobj)
{
g_usleep(100);
g_object_ref (gobj);
g_weak_ref_set(&weak_ref, gobj);
g_object_unref (gobj);
}
int
main(void)
{
GFile *file = g_file_new_for_path ("/");
/* Hacking this to avoid having a whole subtype, but you get the point... */
default_dispose = G_OBJECT_GET_CLASS(file)->dispose;
G_OBJECT_GET_CLASS(file)->dispose = dispose;
g_assert_cmpint(G_OBJECT(file)->ref_count, ==, 1);
g_thread_new("on_other_thread", on_other_thread, file);
g_object_unref(file);
g_print("Cleaning up the object...\n");
g_assert(g_weak_ref_get(&weak_ref) == NULL);
return 0;
}
And we get:
GLib-GObject-Message: 03:21:12.626: FINALIZING Object is 0x562207bb4e00, has weak locations: (nil)
Object disposed 0x562207bb4e00
Completing disposition... 0x562207bb4e00
Cleaning up the object...
GLib-GObject-Message: 03:21:12.628: Reffing 0x562207bb4e00
(process:479719): GLib-GObject-CRITICAL **: 03:21:12.628: g_object_ref: assertion 'G_IS_OBJECT (object)' failed
**
ERROR:/home/marco/Dev/Snippets/gobject-reffing-weak-on-dispose-threaded.c:41:main: assertion failed: (g_weak_ref_get(&weak_ref) == NULL)
Bail out! ERROR:/home/marco/Dev/Snippets/gobject-reffing-weak-on-dispose-threaded.c:41:main: assertion failed: (g_weak_ref_get(&weak_ref) == NULL)
[1] 479719 IOT instruction (core dumped) jhbuild run /tmp/ref-test
Edited by Marco Trevisan