Получение утечки памяти при объединении Vala с C - PullRequest
0 голосов
/ 18 мая 2018

Следующий код Vala в сочетании с C вызывает утечку памяти, и я не могу обернуться вокруг него.

Main.vala

using GLib;

[CCode (cname = "c_function")]
public static extern void c_function (out Tree<int, Tree<int, string>> tree);

public static int main () {

    Tree<int, Tree<int, string>> tree;
    c_function (out tree);
    // If code were to end here and return 0, no memory leak happens, but
    // if we call c_function again, memory leak happens according to valgrind
    c_function (out tree); // Leak happens on this second call
    return 0;
}

main.c

#include <glib.h>

gint treeCompareFunction (gint a, gint b);

void extern c_function (GTree **tree) {
    *tree = g_tree_new ((GCompareFunc)treeCompareFunction);

    for (int i = 0; i < 3; i++) {
        // Memory leak in the next line when function is called a second time
        GTree * nestedTree = g_tree_new ((GCompareFunc)treeCompareFunction); 
        g_tree_insert (nestedTree, i, "value 1");
        g_tree_insert (*tree, i, (gpointer) nestedTree);
    }
}

gint treeCompareFunction (gint a, gint b) {
    if (a < b) return -1;
    if (a == b) return 0;
    return 1;
}

Я не понимаю, почему, если я вызываю функцию C только один раз, не происходит утечка памяти, но если я вызываю ее во второй раз, строка 10 из main.c , который создает дерево в цикле for, вызывает утечку памяти.

Код компилируется с помощью

valac Main.vala main.c -g

, а затем запускается с

valgrind --leak-check=yes ./Main

Хотелось бы узнать, можно ли обойти это.Я попытался очистить дерево в коде Vala перед повторным вызовом функции C.Нет успехаТакже попытался уничтожить дерево, переданное в качестве аргумента, если оно не было NULL при втором вызове функции C.Также нет успеха.По-прежнему возникают утечки памяти.

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Нашел решение.Это ни в коем случае не было тривиальным, так как требовалось посмотреть, что делает Вала, и посмотреть на исходный код gtree.c, чтобы понять, что происходит с выделенной памятью деревьев.

Поскольку Вала вызывает g_tree_unref накорневое дерево по умолчанию в конце программы, корневое дерево освобождается, но фрагменты памяти вложенных деревьев, которые были его частью, теряются и не освобождаются.Надо заставить Валу звонить g_tree_unref на эти вложенные деревья.Чтобы добиться этого, нужно иметь ссылки на вложенные деревья.Это можно сделать в foreach TraverseFunc корневого дерева следующим образом

Main.vala

using GLib;

[CCode (cname = "c_function")]
public static extern void c_function (out Tree<int, Tree<int, string>> tree);

public static int main () {
    Tree<int, Tree<int, string>> tree;
    c_function (out tree);
    // Iterate through the tree and get a strong reference to the values
    // to free them
    tree.@foreach ((TraverseFunc<int, Tree<int, string>>)valueDestroyThroughTraversing);
    c_function (out tree);
    tree.@foreach ((TraverseFunc<int, Tree<int, string>>)valueDestroyThroughTraversing);
    return 0;
}

public bool valueDestroyThroughTraversing (int treeKey, owned Tree<int, string> treeValue) {
    // Do something with the keys and values of the tree if desired
    // treeValue will go out of scope at the end of the method 
    // and Vala will free it
    return false;
}

main.c

#include <stdio.h>
#include <glib.h>

gint treeCompareFunction (gint a, gint b);

void extern c_function (GTree **tree) {
    *tree = g_tree_new ((GCompareFunc)treeCompareFunction);

    for (int i = 0; i < 3; i++) {
        GTree * nestedTree = g_tree_new ((GCompareFunc)treeCompareFunction); 
        g_tree_insert (nestedTree, (gpointer) ((gintptr)i), "value 1");
        g_tree_insert (*tree, (gpointer) ((gintptr)i), nestedTree);
    }
}

gint treeCompareFunction (gint a, gint b) {
    if (a < b) return -1;
    if (a == b) return 0;
    return 1;
}
0 голосов
/ 18 мая 2018

Глядя на код, который вы указали, я хотел бы использовать g_tree_new_full () вместо g_tree_new () в вашем коде C.

Вы повторно используете tree какисходящий аргумент в коде Vala.Таким образом, при втором вызове первое значение, присвоенное tree, должно быть освобождено.Я надеюсь, что Вала сгенерирует вызов, чтобы сделать это, но я не написал никакого примера кода для проверки.Вы можете скомпилировать свой код Vala с помощью переключателя --ccode на valac, чтобы проверить сгенерированный C.

Пока Vala вызывает g_tree_unref (), тогда настройка вашего кода C неосвобождая вложенное дерево.Вам нужна функция GDestroyNotify для передачи вложенного дерева в g_tree_new_full ().

Обновление

Ошибка в вашем C-коде.Ваш код C должен быть:

#include <glib.h>

gint treeCompareFunction (gint a, gint b);

void extern c_function (GTree **tree) {
    *tree = g_tree_new_full ((GCompareFunc)treeCompareFunction,
                             NULL,
                             NULL,
                             g_tree_unref
                             );

    for (int i = 0; i < 3; i++) {
        GTree * nestedTree = g_tree_new ((GCompareFunc)treeCompareFunction);
        g_tree_insert (nestedTree, i, "value 1");
        g_tree_insert (*tree, i, (gpointer) nestedTree);
    }
}

gint treeCompareFunction (gint a, gint b) {
    if (a < b) return -1;
    if (a == b) return 0;
    return 1;
}

Обратите внимание на использование g_tree_unref в качестве функции GDestroyNotify при использовании g_tree_new_full.

Теперь в сводке утечек Valgrind сообщается:

==22035== LEAK SUMMARY:
==22035==    definitely lost: 0 bytes in 0 blocks
==22035==    indirectly lost: 0 bytes in 0 blocks
==22035==      possibly lost: 1,352 bytes in 18 blocks

Раньше, с кодом в вашем вопросе, сводка утечек была:

==21436== LEAK SUMMARY:
==21436==    definitely lost: 288 bytes in 6 blocks
==21436==    indirectly lost: 240 bytes in 6 blocks
==21436==      possibly lost: 1,352 bytes in 18 blocks
...