Skip to content

Commit

Permalink
pythongh-108240: Add PyCapsule_SetTraverse() function
Browse files Browse the repository at this point in the history
The _socket extension uses PyCapsule_SetTraverse() to visit and clear
the socket type in the garbage collector. So the _socket.socket type
can be cleared in some corner cases when it wasn't possible before.
  • Loading branch information
vstinner committed Aug 22, 2023
1 parent a0bb4a3 commit d661ec5
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 72 deletions.
16 changes: 16 additions & 0 deletions Doc/c-api/capsule.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,19 @@ Refer to :ref:`using-capsules` for more information on using these objects.
``NULL``.
Return ``0`` on success. Return nonzero and set an exception on failure.
.. c:function:: int PyCapsule_SetTraverse(PyObject *capsule, traverseproc traverse_func, inquiry clear_func)
Set a traverse and clear functions inside *capsule*.
It can be used if the capsule contains Python objects which should be
visited (and cleared) by the garbage collector (:mod:`gc`). When this
function is called, the capsule is tracked by the GC.
See :c:member:`~PyTypeObject.tp_traverse` and
:c:member:`~PyTypeObject.tp_clear` for the signature of these functions.
Return ``0`` on success. Return nonzero and set an exception on failure.
.. versionadded:: 3.13
1 change: 1 addition & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,10 @@ New Features
:term:`shutting down <interpreter shutdown>`.
(Contributed by Victor Stinner in :gh:`108014`.)

* Add :c:func:`PyCapsule_SetTraverse` function to set GC traverse and clear
functions on a capsule.
(Contributed by Victor Stinner in :gh:`108014`.)

Porting to Python 3.13
----------------------

Expand Down
2 changes: 2 additions & 0 deletions Include/pycapsule.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ PyAPI_FUNC(int) PyCapsule_SetName(PyObject *capsule, const char *name);

PyAPI_FUNC(int) PyCapsule_SetContext(PyObject *capsule, void *context);

PyAPI_FUNC(int) PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_func);

PyAPI_FUNC(void *) PyCapsule_Import(
const char *name, /* UTF-8 encoded string */
int no_block);
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyCapsule_SetTraverse` function to set GC traverse and clear
functions on a capsule. Patch by Victor Stinner.
2 changes: 2 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2450,3 +2450,5 @@
added = '3.13'
[function.PyDict_GetItemStringRef]
added = '3.13'
[function.PyCapsule_SetTraverse]
added = '3.13'
36 changes: 30 additions & 6 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7314,20 +7314,39 @@ os_init(void)
}
#endif

static int
sock_capi_traverse(PyObject *capsule, visitproc visit, void *arg)
{
PySocketModule_APIObject *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
assert(capi != NULL);
Py_VISIT(capi->Sock_Type);
return 0;
}

static int
sock_capi_clear(PyObject *capsule)
{
PySocketModule_APIObject *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
assert(capi != NULL);
Py_CLEAR(capi->Sock_Type);
return 0;
}

static void
sock_free_api(PySocketModule_APIObject *capi)
sock_capi_free(PySocketModule_APIObject *capi)
{
Py_DECREF(capi->Sock_Type);
Py_XDECREF(capi->Sock_Type); // sock_capi_free() can clear it
Py_DECREF(capi->error);
Py_DECREF(capi->timeout_error);
PyMem_Free(capi);
}

static void
sock_destroy_api(PyObject *capsule)
sock_capi_destroy(PyObject *capsule)
{
void *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
sock_free_api(capi);
assert(capi != NULL);
sock_capi_free(capi);
}

static PySocketModule_APIObject *
Expand Down Expand Up @@ -7432,11 +7451,16 @@ socket_exec(PyObject *m)
}
PyObject *capsule = PyCapsule_New(capi,
PySocket_CAPSULE_NAME,
sock_destroy_api);
sock_capi_destroy);
if (capsule == NULL) {
sock_free_api(capi);
sock_capi_free(capi);
goto error;
}
if (PyCapsule_SetTraverse(capsule, sock_capi_traverse, sock_capi_clear) < 0) {
sock_capi_free(capi);
goto error;
}

if (PyModule_Add(m, PySocket_CAPI_NAME, capsule) < 0) {
goto error;
}
Expand Down
Loading

0 comments on commit d661ec5

Please sign in to comment.