Python C Расширение Как проверить подсчет ссылок и утечки памяти? - PullRequest
2 голосов
/ 12 января 2020

Существует ли идиоматический c способ проверки расширения python C, чтобы вы могли с высокой степенью уверенности в правильности подсчета ссылок и отсутствии утечек памяти?

В настоящее время я просто раскомментирую некоторые операторы printf в своих функциях деаллокатора, а затем вручную печатаю различные ситуации, чтобы доказать себе, что мои Py_INCREF и Py_DECREF верны - что ссылка рассчитывает на объекты уменьшились до 0, и, как и ожидалось, вызывался коллокатор. Я ищу способ автоматизировать это в регрессионных тестах.

Например, скажем, у вас есть тип C с именем Foo с оберткой Python с именем PyFoo. A Foo может иметь много детей; каждый ребенок может принадлежать только одному родителю. Когда счетчик дочерних ссылок выходит из области видимости, он не должен освобождать связанный тип C - он должен просто уменьшать родительский тип. Когда счетчик ссылок родителя становится равным 0, он должен отвечать за освобождение связанного C типа

typedef struct {
    PyObject_HEAD

    Foo *wrapped_foo;
    PyObject *parent;
} PyFoo;

/*
* Add a child to a parent, PY_INCREF the parent
* /
static void PyFoo_push(PyFoo *self, PyObject *args, PyObject *kwargs)
{
    int field = 0;
    PyFoo *child = NULL;
    if (!PyArg_ParseTuple(args, "iO!", &field, &FooType, &child)) {
        return NULL;
    }

    Foo_push(self->foo, field, child->foo);

    child->parent = self;
    Py_INCREF(self);
}

static void PyFoo_dealloc(PyFoo *self)
{
    if (self->parent) {
        printf("I'm a child, decreffing my parent\n");
        Py_XDECREF(self->parent);
    } else {
        printf("I'm a parent, freeing my wrapped foo\n");
        Foo_destroy(self->foo);
    }
}

Теперь в Python я напишу что-то вроде следующего, чтобы проверить его работу:

>>> parent = Foo()
>>> child = Foo()
>>> parent['ABC'] = child
>>> del child
I'm a child, decreffing my parent
>>> del parent
I'm a parent, freeing my wrapped foo

>>> parent = Foo()
>>> child = Foo()
>>> parent['ABC'] = child
>>> del parent
>>> del child
I'm a child, decreffing my parent
I'm a parent, freeing my wrapped foo

Я также предоставил функцию для возврата значения Py_REFCNT объекта PyFoo обратно в Python. Я могу написать несколько тестов с этим, но это не так здорово, потому что я действительно не хочу показывать это как частную функцию в моей библиотеке, и это также не учитывает, когда ссылочная учетная запись падает до 0.

...