Я работаю с устаревшей библиотекой C, которую обернул расширением Python C. Библиотека C имеет рекурсивную структуру данных Foo
с API, подобным приведенному ниже:
Foo *Foo_create(void) /* Create new Foo memory */
int Foo_push(Foo *parent, int field, Foo *child) /* Add a child Foo to a parent Foo */
int Foo_destroy(Foo *foo) /* Framework will free all children, caller cannot reuse children after */
Foo *Foo_pop(Foo *parent, int field) /* User responsible for calling Foo_destroy on popped field */
У меня есть структура PyFoo
, которая охватывает Foo
, что-то вроде:
typedef struct {
PyObject_HEAD
Foo *foo;
PyObject *parent;
} PyFoo;
Как и другие функции, которые оборачивают функции Foo_ * и увеличивают / уменьшают соответствующим образом.
Проблема, с которой я столкнулся, заключается в том, что два независимых объекта PyFoo с независимыми ссылками могут указывать на тот же Фу *. Если один из объектов PyFoo выходит из области видимости, он вызывает Foo_destroy, но пользователь может получить доступ ко второму объекту PyFoo и вызвать ошибку сегментации.
Я пытаюсь помешать пользователю моей библиотеки сделать в Python:
parent = Foo() # Foo_create(); parent's refcount is 1
a = Foo() # Foo_create(); a's refcount is 1
parent[1] = a # Foo_push(parent, 1, a); parent's refcount is 2; a's refcount is 1
b = parent.pop(1) # Foo_pop(parent, 1);
# parent's refcount is 1; a's refcount is 1; b's refcount is 1
# a and b's are now independent PyFoo objects with reference count = 1
# HOWEVER both of the *foo pointers point to the same memory
# Delete a, dropping reference count to 0, which calls Foo_destroy
del a # parents refcount is 1; a's refcount is 0; b's refcount is 1
# Access b, which may segfault, since Foo_destroy was called in the last call.
print(b)
Другими словами, a
и b
оба указывают на одну и ту же Foo
память. Однако они являются независимыми Python объектами с независимыми счетами. Как только a
выходит из области видимости, он уничтожает память, на которую указывает b
. Доступ к b
, вероятно, приведет к segfault.
Кажется, что это будет распространенной проблемой при написании Python Extensions.
Полагаю, мне нужен способ подсчета ссылок на указателе Foo. Например, a
и b
должны иметь одинаковую идентичность в приведенном выше примере. Или, возможно, мне нужна какая-то структура данных, которая подсчитывает количество PyFoos, которые используют один и тот же указатель Foo, а Foo_destroy вызывается только тогда, когда количество указателей Foo падает до 0.
Что такое идиоматизм c способ решить эту проблему?
Вот соответствующий сценарий в C:
Foo *parent = Foo_create();
Foo *a = Foo_create();
Foo_push(parent, 1, a);
Foo *b = Foo_pop(parent, 1);
/* a and b both point to same memory */
Foo_destroy(a);
/* better not access b after this */
a = NULL;
b = NULL;