Details
[Home]
Issue of the Implementation # D0005
Brief
Memory leak upon calling "g_main_loop_run" in the secondary thread, while main loop is already running
Detailed Description
When g_main_loop_run is being called in the secondary thread, while g_main_loop_run was already called and loop is running, the following part of code is being executed:
//********************************* ... if (!loop->context->cond) loop->context->cond = g_cond_new (); ... //*********************************This allocated condition is not being freed upon calling g_main_context_unref, and 48 bytes are being lost according to Valgrind. When POSIX threads are being used, g_cond_new () allocates internally a "pthread_cond_t" structure with sizeof(pthread_cond_t) = 48.
Problem location(s) in the standard
Linux Standard Base Desktop Specification 3.1, Chapter 12. Libraries, 12.2 Interfaces for libglib-2.0; http://www.gtk.org/api/2.6/glib/glib-The-Main-Event-Loop.html#g-main-loop-run
Reproducing
Two threads are started in this example:
- First thread creates a new main event loop and it's context, adds a sample event source and runs the loop.
- Whenever first thread finishes it's initialization the second thread is started, and calls "g_main_loop_run".
- After 3 seconds main loop and context are unreferenced.
Example
#include <stdlib.h> #include <signal.h> #include <errno.h> #include <time.h> #include <semaphore.h> #include <stdio.h> #include <glib.h> #define _DECLARE_G_SEMAPHORE(s_name) sem_t s_name; #define _G_SUNLOCK(s_name) sem_post(&s_name); #define _G_STIMED_WAIT(seconds, s_name, exp_var, tumeout_st)\ int exp_var=0;struct timespec tumeout_st;\ clock_gettime(CLOCK_REALTIME, &tumeout_st);tumeout_st.tv_sec += seconds;\ if(sem_timedwait(&s_name, &tumeout_st) != 0)\ {if(errno == ETIMEDOUT) exp_var=1;} #define _G_STIMED_WAIT_2(seconds, s_name, exp_var, tumeout_st)\ exp_var=0;clock_gettime(CLOCK_REALTIME, &tumeout_st);\ tumeout_st.tv_sec += seconds;\ if(sem_timedwait(&s_name, &tumeout_st) != 0)\ {if(errno == ETIMEDOUT) exp_var=1;} #define _G_STIMED_WAIT_EXPIRED(exp_var) exp_var #define DECLARE_G_SEMAPHORE _DECLARE_G_SEMAPHORE(_g_sem_) #define G_SUNLOCK _G_SUNLOCK(_g_sem_) #define G_STIMED_WAIT(seconds) _G_STIMED_WAIT\ (seconds, _g_sem_, __stw_expired, __stimeout) #define G_STIMED_WAIT_2(seconds) _G_STIMED_WAIT_2\ (seconds, _g_sem_, __stw_expired, __stimeout) #define G_STIMED_WAIT_EXPIRED _G_STIMED_WAIT_EXPIRED(__stw_expired) #define DECLARE_G_SEMAPHORE_EX _DECLARE_G_SEMAPHORE(_g_sem_2) #define G_SUNLOCK_EX _G_SUNLOCK(_g_sem_2) #define G_STIMED_WAIT_EX(seconds) _G_STIMED_WAIT\ (seconds, _g_sem_2, __stw_expired_2, __stimeout_2) #define G_STIMED_WAIT_2_EX(seconds) _G_STIMED_WAIT_2\ (seconds, _g_sem_2, __stw_expired_2, __stimeout_2) #define G_STIMED_WAIT_EXPIRED_EX _G_STIMED_WAIT_EXPIRED(__stw_expired_2) #define DECLARE_G_SEMAPHORE_EX_2 _DECLARE_G_SEMAPHORE(_g_sem_3) #define G_SUNLOCK_EX_2 _G_SUNLOCK(_g_sem_3) #define G_STIMED_WAIT_EX_2(seconds) _G_STIMED_WAIT\ (seconds, _g_sem_3, __stw_expired_3, __stimeout_3) #define G_STIMED_WAIT_2_EX_2(seconds) _G_STIMED_WAIT_2\ (seconds, _g_sem_3, __stw_expired_3, __stimeout_3) #define G_STIMED_WAIT_EXPIRED_EX_2 _G_STIMED_WAIT_EXPIRED(__stw_expired_3) DECLARE_G_SEMAPHORE DECLARE_G_SEMAPHORE_EX DECLARE_G_SEMAPHORE_EX_2 void initGlobalSemaphores() { sem_init(&_g_sem_, 0, 0); sem_init(&_g_sem_2, 0, 0); sem_init(&_g_sem_3, 0, 0); } GThread* _g_thread_create(GThreadFunc func, gpointer data, gboolean joinable, GError **error) { return g_thread_create_full(func, data, 0, joinable, FALSE, G_THREAD_PRIORITY_NORMAL, error); } GMainLoop* mlrMainLoop = NULL; GMainContext* mlrMainContext = NULL; gboolean mlr_callback(gpointer data){G_STIMED_WAIT(10000); return TRUE;} gboolean mlr_prepare(GSource *source, gint *timeout_){return TRUE;} gboolean mlr_check(GSource *source){return TRUE;} gboolean mlr_dispatch(GSource *source, GSourceFunc callback, gpointer user_data){return callback(user_data);} GSourceFuncs mlr_funcs = { mlr_prepare, mlr_check, mlr_dispatch, NULL }; int mlr_status[2] = {0, 0}; gpointer mlr_loop_main(gpointer data) { mlrMainContext = g_main_context_new(); if(mlrMainContext == NULL) { mlr_status[0] = -1; return NULL; } mlrMainLoop = g_main_loop_new(mlrMainContext, FALSE); if(mlrMainLoop == NULL) { mlr_status[0] = -2; return NULL; } GSource *mlr_source = (GSource*)g_source_new(&mlr_funcs, sizeof (GSource)); if(mlr_source == NULL) { mlr_status[0] = -3; return NULL; } g_source_set_callback (mlr_source, mlr_callback, NULL, NULL); g_source_attach(mlr_source, mlrMainContext); g_source_unref(mlr_source); mlr_status[0] = 1; G_SUNLOCK_EX; g_main_loop_run(mlrMainLoop); mlr_status[0] = 2; G_SUNLOCK_EX; return NULL; } gpointer mlr_loop_2nd(gpointer data) { mlr_status[1] = 1; g_main_loop_run(mlrMainLoop); mlr_status[1] = 2; G_SUNLOCK_EX_2; return NULL; } GMainContext *chk_mainContext; int main() { if(!g_thread_supported()) g_thread_init(NULL); initGlobalSemaphores(); g_thread_create(mlr_loop_main, NULL, TRUE, NULL); //Waiting for the first thread to wrap up the initialization G_STIMED_WAIT_EX(2); g_thread_create(mlr_loop_2nd, NULL, TRUE, NULL); g_usleep(3000000); g_main_loop_quit(mlrMainLoop); //Waiting for the second thread to finish G_STIMED_WAIT_EX_2(1); G_SUNLOCK //Waiting for the first thread to finish G_STIMED_WAIT_2_EX(1); if(mlrMainLoop) g_main_loop_unref(mlrMainLoop); if(mlrMainContext) g_main_context_unref(mlrMainContext); return 0; }
Possible solutions
The following modification is proposed in the file "gmain.c".
--- glib-2.14.0/glib/gmain.c +++ glib-2.14.0-fixed/glib/gmain.c @@ -665,6 +665,11 @@ else main_contexts_without_pipe = g_slist_remove (main_contexts_without_pipe, context); + + if(context->cond != NULL) + { + g_cond_free(context->cond); + } #endif g_free (context);
Component
gtk-glib 2.6.2 or later
Accepted
Gnome Bugzilla 479724
Status
Fixed in gtk-glib - 2.15.0
[Home]