پشتیبانی از زباله‌روبی چرخه‌ای

پشتیبانی پایتون از تشخیص و جمع‌آوری زباله‌هایی که شامل ارجاع‌های چرخه‌ای هستند، نیازمند پشتیبانی از سوی نوع‌های شیء است که «ظرف»هایی برای اشیاء دیگر به شمار می‌روند و ممکن است خود آن اشیاء نیز ظرف باشند. نوع‌هایی که ارجاعی به اشیاء دیگر ذخیره نمی‌کنند، یا تنها ارجاع‌هایی به نوع‌های اتمی (مانند اعداد یا رشته‌ها) ذخیره می‌کنند، نیازی به ارائه هیچ پشتیبانی صریحی برای زباله‌روبی ندارند.

برای ایجاد یک نوع ظرف، فیلد tp_flags شیء نوع باید شامل Py_TPFLAGS_HAVE_GC باشد و پیاده‌سازی هندلر tp_traverse ارائه شود. اگر نمونه‌های نوع تغییرپذیر باشند، پیاده‌سازی tp_clear نیز باید ارائه شود.

Py_TPFLAGS_HAVE_GC

اشیایی با نوعی که این پرچم در آن تنظیم شده است باید با قواعدی که در اینجا مستند شده‌اند مطابقت داشته باشند. برای سهولت، به این اشیاء «اشیاء ظرف» گفته خواهد شد.

سازنده‌های انواع ظرف باید از دو قاعده پیروی کنند:

  1. حافظه برای شیء باید با استفاده از PyObject_GC_New یا PyObject_GC_NewVar تخصیص یابد.

  2. پس از آنکه تمامی فیلدهایی که ممکن است حاوی ارجاع‌هایی به ظرف‌های دیگر باشند مقداردهی اولیه شدند، باید PyObject_GC_Track() را فراخوانی کند.

به‌طور مشابه، آزادساز حافظه‌ی شیء باید از جفت قاعده‌ی مشابهی پیروی کند:

  1. پیش از آنکه فیلد‌هایی که به ظرف‌های دیگر ارجاع دارند نامعتبر شوند، باید PyObject_GC_UnTrack() فراخوانی شود.

  2. حافظه‌ی شیء باید با استفاده از PyObject_GC_Del() آزاد شود.

    هشدار

    اگر نوعی Py_TPFLAGS_HAVE_GC را اضافه کند، آن‌گاه باید حداقل یک هندلر tp_traverse را پیاده‌سازی کند یا صریحاً از هندلری در زیرکلاس یا زیرکلاس‌های خود استفاده کند.

    هنگام فراخوانی PyType_Ready() یا برخی از API‌هایی که به‌طور غیرمستقیم آن را فراخوانی می‌کنند، مانند PyType_FromSpecWithBases() یا PyType_FromSpec()، اگر نوع از کلاسی ارث‌بری کند که پروتکل زباله‌روب را پیاده‌سازی کرده باشد و کلاس فرزند شامل پرچم Py_TPFLAGS_HAVE_GC نباشد، مفسر به‌طور خودکار فیلدهای tp_flags، tp_traverse و tp_clear را پر می‌کند.

PyObject_GC_New(TYPE, typeobj)

مشابه PyObject_New است، اما برای اشیاء ظرفی است که پرچم Py_TPFLAGS_HAVE_GC روی آن‌ها تنظیم شده است.

برای تخصیص حافظه به یک شیء، این را مستقیماً فراخوانی نکنید؛ در عوض، جایگاه tp_alloc نوع را فراخوانی کنید.

هنگام پر کردن جایگاه tp_alloc یک نوع، PyType_GenericAlloc() به تابع سفارشی‌ای که صرفاً این ماکرو را فراخوانی می‌کند ترجیح داده می‌شود.

حافظه‌ی تخصیص‌یافته توسط این ماکرو باید با PyObject_GC_Del() آزاد شود (که معمولاً از طریق جایگاه tp_free شیء فراخوانی می‌شود).

همچنین ملاحظه نمائید

PyObject_GC_NewVar(TYPE, typeobj, size)

مشابه PyObject_NewVar است، اما برای اشیاء ظرفی که پرچم Py_TPFLAGS_HAVE_GC برایشان تنظیم شده است.

برای تخصیص حافظه به یک شیء، این را مستقیماً فراخوانی نکنید؛ در عوض، جایگاه tp_alloc نوع را فراخوانی کنید.

هنگام پر کردن جایگاه tp_alloc یک نوع، PyType_GenericAlloc() به تابع سفارشی‌ای که صرفاً این ماکرو را فراخوانی می‌کند ترجیح داده می‌شود.

حافظه‌ی تخصیص‌یافته توسط این ماکرو باید با PyObject_GC_Del() آزاد شود (که معمولاً از طریق جایگاه tp_free شیء فراخوانی می‌شود).

همچنین ملاحظه نمائید

PyObject *PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
این است API ناپایداراین ممکن است بدون هشدار در نسخه‌های جزئی تغییر کند.

مشابه PyObject_GC_New است، اما extra_size بایت در انتهای شیء تخصیص می‌دهد (در آفست tp_basicsize). حافظه‌ی تخصیص‌یافته با صفر مقداردهی اولیه می‌شود، به‌جز سرآیند شیء پایتون.

داده‌ی اضافی به همراه شیء آزادسازی خواهد شد، اما در غیر این صورت توسط پایتون مدیریت نمی‌شود.

حافظه‌ی تخصیص‌یافته توسط این تابع باید با PyObject_GC_Del() آزاد شود (که معمولاً از طریق جایگاه tp_free شیء فراخوانی می‌شود).

هشدار

این تابع به‌عنوان ناپاید علامت‌گذاری شده است زیرا سازوکار نهایی برای رزرو داده‌های اضافی پس از یک نمونه هنوز تصمیم‌گیری نشده است. برای تخصیص تعداد متغیری از فیلدها، بهتر است به‌جای آن از PyVarObject و tp_itemsize استفاده کنید.

اضافه شده در نسخه‌ی 3.12.

PyObject_GC_Resize(TYPE, op, newsize)

تغییر اندازه‌ی شیء‌ای که توسط PyObject_NewVar تخصیص‌یافته است. شیء تغییراندازه‌یافته از نوع TYPE* (منظور هر نوع C است) را برمی‌گرداند یا در صورت شکست NULL.

op باید از نوع PyVarObject* باشد و هنوز توسط جمع‌کننده زباله پیگیری نشده باشد. newsize باید از نوع Py_ssize_t باشد.

void PyObject_GC_Track(PyObject *op)
قسمتی از ABI پایدار.

شیء op را به مجموعه‌ی اشیاء ظرفی که توسط زباله‌روب پیگیری می‌شوند اضافه می‌کند. زباله‌روب می‌تواند در زمان‌های غیرمنتظره اجرا شود، بنابراین اشیاء باید در حین پیگیری معتبر باشند. این تابع باید پس از معتبر شدن همه‌ی فیلدهایی که هندلر tp_traverse دنبال می‌کند فراخوانی شود، که معمولاً نزدیک به پایان سازنده انجام می‌شود.

int PyObject_IS_GC(PyObject *obj)

اگر شیء پروتکل جمع‌آورنده زباله را پیاده‌سازی کند، مقدار غیرصفر برمی‌گرداند؛ در غیر این صورت ۰ برمی‌گرداند.

اگر این تابع مقدار 0 را برگرداند، شیء توسط زباله‌روب قابل پیگیری نخواهد بود.

int PyObject_GC_IsTracked(PyObject *op)
قسمتی از ABI پایدار از نسخه‌ی 3.9.

اگر نوع شیء op پروتکل GC را پیاده‌سازی کرده باشد و op در حال حاضر توسط زباله‌روب پیگیری شود، ۱ را برمی‌گرداند و در غیر این صورت ۰.

این مشابه تابع پایتون gc.is_tracked() است.

اضافه شده در نسخه‌ی 3.9.

int PyObject_GC_IsFinalized(PyObject *op)
قسمتی از ABI پایدار از نسخه‌ی 3.9.

اگر نوع شیء op پروتکل زباله‌روبی را پیاده‌سازی کرده باشد و op قبلاً توسط زباله‌روب نهایی‌سازی شده باشد، مقدار ۱ و در غیر این صورت مقدار ۰ را برمی‌گرداند.

این مشابه تابع پایتونی gc.is_finalized() است.

اضافه شده در نسخه‌ی 3.9.

void PyObject_GC_Del(void *op)
قسمتی از ABI پایدار.

حافظه‌ای را که با PyObject_GC_New یا PyObject_GC_NewVar به یک شیء تخصیص یافته است، آزاد می‌کند.

برای آزاد کردن حافظه‌ی یک شیء، این را مستقیماً فراخوانی نکنید؛ در عوض جایگاه tp_free نوع را فراخوانی کنید.

از این برای حافظه‌ی تخصیص‌یافته توسط PyObject_New، PyObject_NewVar یا توابع تخصیص مرتبط استفاده نکنید؛ به جای آن از PyObject_Free() استفاده کنید.

همچنین ملاحظه نمائید

void PyObject_GC_UnTrack(void *op)
قسمتی از ABI پایدار.

شیء op را از مجموعه‌ی اشیای ظرفی که توسط جمع‌کننده پیگیری می‌شوند حذف می‌کند. توجه داشته باشید که PyObject_GC_Track() می‌تواند دوباره روی این شیء فراخوانی شود تا آن را به مجموعه‌ی اشیای پیگیری‌شده بازگرداند. آزادساز حافظه‌ی (هندلر tp_dealloc) باید این تابع را برای این شیء پیش از آنکه هر یک از فیلدهای مورد استفاده‌ی هندلر tp_traverse نامعتبر شوند، فراخوانی کند.

تغییر یافته در نسخه‌ی 3.8: ماکروهای _PyObject_GC_TRACK() و _PyObject_GC_UNTRACK() از API عمومی C حذف شده‌اند.

هندلر tp_traverse یک پارامتر تابع از این نوع می‌پذیرد:

typedef int (*visitproc)(PyObject *object, void *arg)
قسمتی از ABI پایدار.

نوع تابع بازدیدکننده (visitor function) که به هندلر tp_traverse پاس داده می‌شود. این تابع باید با یک شیء برای پیمایش به‌عنوان object و پارامتر سوم هندلر tp_traverse به‌عنوان arg فراخوانی شود. هسته‌ی پایتون از چندین تابع بازدیدکننده برای پیاده‌سازی تشخیص زباله‌ی چرخه‌ای استفاده می‌کند؛ انتظار نمی‌رود که کاربران نیازی به نوشتن توابع بازدیدکننده‌ی خودشان داشته باشند.

هندلر tp_traverse باید از نوع زیر باشد:

typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)
قسمتی از ABI پایدار.

تابع پیمایش برای یک شیء ظرف. پیاده‌سازی‌ها باید تابع visit را برای هر شیء‌ای که مستقیماً توسط self دربرگرفته شده است فراخوانی کنند، به‌طوری که پارامترهای visit، شیء دربرگرفته و مقدار arg*ِ ارسال‌شده به هندلر باشند. تابع *visit نباید با آرگومان شیء NULL فراخوانی شود. اگر visit مقدار غیرصفر برگرداند، آن مقدار باید بی‌درنگ بازگردانده شود.

تابع پیمایش نباید هیچ اثر جانبی داشته باشد. پیاده‌سازی‌ها نباید شمارش ارجاع هیچ‌یک از اشیاء پایتون را تغییر دهند و نباید هیچ شیء پایتونی ایجاد یا نابود کنند.

برای ساده‌سازی نوشتن هندلرهای tp_traverse، ماکروی Py_VISIT() فراهم شده است. برای استفاده از این ماکرو، پیاده‌سازی tp_traverse باید آرگومان‌های خود را دقیقاً visit و arg نام‌گذاری کند:

Py_VISIT(o)

اگر PyObject* o برابر NULL نباشد، کال‌بک visit با آرگومان‌های o و arg فراخوانی می‌شود. اگر visit مقدار غیرصفری برگرداند، همان مقدار برگردانده می‌شود. با استفاده از این ماکرو، هندلرهای tp_traverse به این شکل هستند:

static int
my_traverse(Noddy *self, visitproc visit, void *arg)
{
    Py_VISIT(self->foo);
    Py_VISIT(self->bar);
    return 0;
}

هندلر tp_clear باید از نوع inquiry باشد، یا در صورتی که شیء تغییرناپذیر است، NULL باشد.

typedef int (*inquiry)(PyObject *self)
قسمتی از ABI پایدار.

ارجاع‌هایی را که ممکن است چرخه‌های ارجاع ایجاد کرده باشند رها کنید. اشیاء تغییرناپذیر نیازی به تعریف این متد ندارند، زیرا هرگز نمی‌توانند به‌طور مستقیم چرخه‌های ارجاع ایجاد کنند. توجه داشته باشید که شیء باید پس از فراخوانی این متد همچنان معتبر باشد (تنها Py_DECREF() را روی یک ارجاع فراخوانی نکنید). زباله‌روب در صورتی که تشخیص دهد این شیء در یک چرخه ارجاع درگیر است، این متد را فراخوانی خواهد کرد.

کنترل وضعیت زباله‌روب

API زبان C توابع زیر را برای کنترل اجراهای زباله‌روبی فراهم می‌کند.

Py_ssize_t PyGC_Collect(void)
قسمتی از ABI پایدار.

در صورت فعال بودن زباله‌روب، یک زباله‌روبی کامل انجام می‌دهد. (توجه داشته باشید که gc.collect() آن را بدون قید و شرط اجرا می‌کند.)

تعداد اشیاء جمع‌آوری‌شده + اشیاء دسترس‌ناپذیری که نمی‌توان آن‌ها را جمع‌آوری کرد را برمی‌گرداند. اگر زباله‌روب غیرفعال باشد یا از قبل در حال جمع‌آوری باشد، بلافاصله 0 را برمی‌گرداند. خطاهای رخ‌داده در حین زباله‌روبی به sys.unraisablehook منتقل می‌شوند. این تابع استثنایی ایجاد نمی‌کند.

int PyGC_Enable(void)
قسمتی از ABI پایدار از نسخه‌ی 3.10.

فعال کردن زباله‌روب: مشابه gc.enable(). وضعیت قبلی را برمی‌گرداند، ۰ برای غیرفعال و ۱ برای فعال.

اضافه شده در نسخه‌ی 3.10.

int PyGC_Disable(void)
قسمتی از ABI پایدار از نسخه‌ی 3.10.

جمع‌کننده زباله را غیرفعال می‌کند: مشابه gc.disable(). وضعیت قبلی را برمی‌گرداند، 0 برای غیرفعال و 1 برای فعال.

اضافه شده در نسخه‌ی 3.10.

int PyGC_IsEnabled(void)
قسمتی از ABI پایدار از نسخه‌ی 3.10.

وضعیت زباله‌روب را پرس‌وجو می‌کند: مشابه gc.isenabled(). وضعیت فعلی را برمی‌گرداند، ۰ برای غیرفعال و ۱ برای فعال.

اضافه شده در نسخه‌ی 3.10.

پرس‌وجوی وضعیت زباله‌روب

C-API رابط زیر را برای استعلام اطلاعات درباره‌ی زباله‌روب فراهم می‌کند.

void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
این است API ناپایداراین ممکن است بدون هشدار در نسخه‌های جزئی تغییر کند.

callback داده‌شده را روی تمام اشیای زنده‌ی قابل زباله‌روبی اجرا می‌کند. arg بدون تغییر به تمام فراخوانی‌های callback پاس داده می‌شود.

هشدار

اگر اشیاء جدیدی توسط کال‌بک تخصیص داده (یا آزادسازی) شوند، تعریف‌نشده است که آیا بازدید خواهند شد یا خیر.

زباله‌روبی در حین عملیات غیرفعال است. اجرای صریح یک زباله‌روبی در کال‌بک ممکن است به رفتار تعریف‌نشده منجر شود؛ برای مثال، پیمایش همان اشیاء چندین بار یا اصلاً پیمایش‌نشدن آن‌ها.

اضافه شده در نسخه‌ی 3.12.

typedef int (*gcvisitobjects_t)(PyObject *object, void *arg)

نوع تابع بازدیدکننده‌ای که باید به PyUnstable_GC_VisitObjects() پاس داده شود. arg همان argی است که به PyUnstable_GC_VisitObjects پاس داده شده است. برای ادامه‌ی پیمایش 1 را برگردانید و برای توقف پیمایش 0 را برگردانید. سایر مقادیر بازگشتی فعلاً رزرو شده‌اند، بنابراین رفتار در صورت برگرداندن هر مقدار دیگر تعریف‌نشده است.

اضافه شده در نسخه‌ی 3.12.