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]