6

Два раздела документа Python 2.7, упомянутые добавлением сборник циклических мусора (CGC) поддержка объектов контейнера, определенных в модулях расширения.Правильная циклическая сборка мусора в модулях расширения

Python/C API Reference Manual дает два правила, т.е.

  1. памяти для объекта должна быть выделена с помощью PyObject_GC_New() или PyObject_GC_NewVar().
  2. После того, как все поля, которые могут содержать ссылки на другие контейнеры, будут инициализированы, он должен вызвать PyObject_GC_Track().

В то время как в Extending and Embedding the Python Interpreter, для Noddy например, кажется, что добавление Py_TPFLAGS_HAVE_GC флага и заполнение tp_traverse и tp_clear слотов будет вполне достаточно, чтобы обеспечить поддержку CGC. И два вышеприведенных правила НЕ применяются практически.

Когда я изменил Noddy пример на самом деле следовать правилам PyObject_GC_New()/PyObject_GC_Del() и PyObject_Track()/PyObject_GC_UnTrack(), он неожиданно поднял ошибку утверждение говоря,

Модули/gcmodule.c: 348: visit_decref: Утверждение «дс -> gc.gc_refs! = 0 "не удалось. refcount был слишком мал

Это приводит к моему путанице в правильном/безопасном способе реализации CGC. Может ли кто-нибудь давать советы или, предпочтительно, neat пример контейнерного объекта с поддержкой CGC?

ответ

1

В большинстве нормальных обстоятельств вам не нужно делать слежения/слежения за собой. Это описано в документации, однако это не делается четко. В случае с Noddy example вы определенно этого не делаете.

Короткий вариант заключается в том, что объект TypeObject содержит два указателя на функции: tp_alloc и tp_free.По умолчанию tp_alloc вызывает все правильные функции при создании класса (если установлен Py_TPFLAGS_HAVE_GC), а tp_free не изучает класс при уничтожении.

Noddy documentation says (в конце раздела):

Это довольно много его. Если бы мы написали пользовательские слоты tp_alloc или tp_free, нам нужно будет их модифицировать для сбора циклических мусора. Большинство расширений будут использовать предоставленные версии автоматически.

К сожалению, одно место, которое не дает понять, что вам не нужно это делать самостоятельно, является Supporting Cyclic Garbage Collection documentation.


деталь:

Нодди, распределяются с использованием функции под названием Noddy_new положить в tp_new пазы TypeObject. Согласно the documentation, главное, что должна выполнить «новая» функция, это вызвать tp_alloc slot. Обычно вы не пишете tp_alloc, а по умолчанию он равен PyType_GenericAlloc().

В обзоре PyType_GenericAlloc() in the Python source показан ряд разделов, где оно изменяется на основании PyType_IS_GC(type). Сначала он вызывает _PyObject_GC_Malloc вместо PyObject_Malloc, а затем он вызывает _PyObject_GC_TRACK(obj). [Обратите внимание, что все, что PyObject_New действительно делает вызов PyObject_Malloc, а затем tp_init.]

Аналогично, на открепления вызове tp_free slot, который автоматически устанавливается в PyObject_GC_Del для классов с Py_TPFLAGS_HAVE_GC. PyObject_GC_Del содержит код, который делает то же самое, что и PyObject_GC_UnTrack, поэтому вызов для проверки не нужен.

3

Я сам не достаточно опыт в API C, чтобы дать вам какие-либо советы, но есть множество примеров в самих реализациях контейнера Python.

Лично я бы начал с реализации кортежа первым, так как он неизменен: Objects/tupleobject.c. Затем переходите к реализациям dict, list и set для дальнейших примечаний по изменяемым контейнерам:

Я не могу не заметить, что есть звонки до PyObject_GC_New(), PyObject_GC_NewVar() и PyObject_GC_Track() по всему, а также с Py_TPFLAGS_HAVE_GC установлен.

+1

Спасибо за ответ. Я изучаю возможность того, что некоторые версии API-интерфейса PyObject_GC_New() не могут корректно обрабатывать подтипы, что является причиной AssertionError. – liuyu

 Смежные вопросы

  • Нет связанных вопросов^_^