Callback races when GSource is destructed
The problem was first observed on Arm64 platform with old glib 2.48.0, glib 2.48.1 and libsigc++ 2.8.0. Still reproducible with latest glib 2.60.0, glibmm 2.60.0 and libsigc++ 2.10.1.
When on one thread g_main_context_iterate() just checked that GSource is still active and has valid callback to call in g_main_context_check() it unlocks context and calls source->source_funcs->check() function. In the same time the GSource can be destructed and SourceConnectionNode::notify() is called that calls g_source_destroy() that clears the G_HOOK_FLAG_ACTIVE flag and resets source->callback_data and source->callback_func pointers to NULL. When Source::check_vfunc() will call get_wrapper() and then glibmm_source_get_callback_data() it will return nullptr, because of source->callback_funcs ara already nullptr. The crash will happen in Source::get_wrapper() when accessing nullptr data pointer:
Source* Source::get_wrapper(GSource* source)
{
SourceCallbackData* const data = glibmm_source_get_callback_data(source); // data = nullptr
return data->wrapper; <<<<<<<<<<<<<<<<<<<<<<<<< nullptr->wrapper
}
static SourceCallbackData*
glibmm_source_get_callback_data(GSource* source)
{
g_return_val_if_fail(source->callback_funcs != nullptr, nullptr); <<<<<<<<<<<<<<<<<<<<<<<< source->callback_funcs == nullptr
//...
Even if glib is recently improving some thread safety in latest master branch, none of that would help in this situation. Glibmm can avoid races between checking structure members and using them by copying them to local values. Since it is not unexpected for get_wrapper / glibmm_source_get_callback_data() to return nullptr then all users should check pointer before use. This fix helps for both 2.6.x and 2.48.x libs.