Подробности
[В начало]
Проблема в реализации № 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
Способ воспроизведения
В примере производится запуск двух потоков:
- Первый поток создает основной цикл и контекст, добавляет к нему экземпляр "источника событий" и запускает цикл.
- После того как первый поток завершает инициализацию запускается второй поток, вызывающий "g_main_loop_run".
- По прошествии 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
[В начало]