Like the GWeakRef's in GObject, there is a global lock that is consulted whenever g_main_context_unref() or g_source_destroy() or g_source_unref() is called to retrieve a reference to the associated GMainContext.
There are a number of actual races that are fixed by this change.
- Racing GSource destruction with g_main_context_unref() is solved by holding the global source_weak_locations lock while setting source->context = NULL and while g_source_destroy() attempts to retrieve source->context;
- Same race as 1. but inside g_source_unref()
- Theoretical race of double freeing the contents of context->pending_dispatches if both g_source_destroy() and g_main_context_unref() both enter g_main_context_unref().
A couple of implementation notes:
- Unlocking source_weak_locations too early in g_main_context_unref() (before g_source_destroy_internal() is called) may have a race of the G_HOOK_FLAG_ACTIVE state of the source and cause a leak of the source. This is why source_weak_locations is also held over the calls to g_source_destroy_internal() in g_main_context_unref(). So that either g_main_context_unref() or g_source_destroy()
- g_main_context_unref() now needs to be more of a dispose() implementation as it can be called multiple times with losing the last ref.
Looking at this I have found another potential
g_source_destroy() race with returning
dispatch() that also needs looking at.