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:

  1. First thread creates a new main event loop and it's context, adds a sample event source and runs the loop.
  2. Whenever first thread finishes it's initialization the second thread is started, and calls "g_main_loop_run".
  3. 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]