Подробности

[В начало]

Проблема в реализации № D0005

Краткое описание

Утечка памяти при вызове функции "g_main_loop_run" из вспомогательного потока, в то время как "основной цикл" уже был запущен

Подробное описание

При вызове функции g_main_loop_run из вспомогательного потока, в то время как еще один вызов g_main_loop_run был уже осуществлен из основного потока и "цикл" уже запущен, происходит выполнение следующих строк кода:

//*********************************
...
  if (!loop->context->cond)
    loop->context->cond = g_cond_new ();
...
//*********************************
Ресурсы данного поля из внутренней структоры контекста не освобождаются при вызове g_main_context_unref и судя по отчету Valgrind-а происходит утечка 48 байт памяти. При использовании потоков в реализации POSIX это соответсвует размеру внутренней структуры sizeof(pthread_cond_t) = 48.

Раздел стандарта

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

Способ воспроизведения

В примере производится запуск двух потоков:

  1. Первый поток создает основной цикл и контекст, добавляет к нему экземпляр "источника событий" и запускает цикл.
  2. После того как первый поток завершает инициализацию запускается второй поток, вызывающий "g_main_loop_run".
  3. По прошествии 3-х секунд работа запущенных потоков приостанавливается и ресурсы занятые основным циклом и контекстом освобождаются.

Пример

#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;
}

Способы устранения

Предлагается произвести следующие изменения в файле "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);

Компонент

gtk-glib 2.6.2 or later

Принято

Gnome Bugzilla 479724

Статус

Исправлено в gtk-glib - 2.15.0

[В начало]