This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: ctypes.cast() creates circular reference in original object
Type: resource usage Stage: needs patch
Components: ctypes Versions: Python 3.10, Python 3.9, Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: bgilbert, mark.dickinson, terry.reedy, theller, vinay.sajip
Priority: normal Keywords:

Created on 2011-08-24 21:53 by bgilbert, last changed 2022-04-11 14:57 by admin.

Messages (5)
msg142933 - (view) Author: Benjamin Gilbert (bgilbert) Date: 2011-08-24 21:53
Section 15.17.1.15 of the ctypes documentation illustrates the use of cast() thusly:

>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>

Executing the cast() causes a._objects to gain a reference back to a:

>>> a = (c_byte * 4)()
>>> a._objects
>>> cast(a, POINTER(c_int))
<__main__.LP_c_int object at 0x7fb879065b90>
>>> a._objects
{140430281170384: <__main__.c_byte_Array_4 object at 0x7fb879065dd0>}
>>> a
<__main__.c_byte_Array_4 object at 0x7fb879065dd0>
>>> 

If large temporary arrays are allocated, cast, used, and discarded in a tight inner loop, a lot of memory can thus be consumed by self-referencing objects before the garbage collector has a chance to run.  Even if this behavior is correct from the perspective of ctypes, it is surprising.
msg143117 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2011-08-28 18:39
What action are you suggesting? Change ctypes code or its doc or something else. If the doc, please suggest a specific change.

Can you test on 3.x?
msg143155 - (view) Author: Vinay Sajip (vinay.sajip) * (Python committer) Date: 2011-08-29 16:15
I can confirm that the same behaviour occur in Python 3.3, and this appears to be by design. There's a specific line in the cast() function in in Modules/_ctypes.c:

rc = PyDict_SetItem(result->b_objects, index, src);

This adds the source object to the _objects of the cast object being returned. However, earlier in the function, the code

CDataObject *obj = (CDataObject *)src;

...

result->b_objects = obj->b_objects;

ensures that result and src are using a single dictionary. Possibly, the result's b_objects needs to be a copy of the src's b_objects; but I don't know enough about ctypes internals to say whether the present code is intentional, or whether an attempt to minimise resource usage (by not copying the dictionary) has led to unwanted consequences.
msg205955 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-12-12 14:26
> Possibly, the result's b_objects needs to be a copy of the src's b_objects

That sounds right to me.  I can't really believe that the current behaviour is intentional.
msg205961 - (view) Author: Vinay Sajip (vinay.sajip) * (Python committer) Date: 2013-12-12 16:47
Adding Thomas Heller to nosy to see if he can shed any light on this.
History
Date User Action Args
2022-04-11 14:57:21adminsetgithub: 57045
2020-09-30 03:51:20eryksunsetstage: needs patch
versions: + Python 3.8, Python 3.9, Python 3.10, - Python 2.7, Python 3.3
2013-12-12 16:47:59vinay.sajipsetnosy: + theller
messages: + msg205961
2013-12-12 14:26:39mark.dickinsonsetnosy: + mark.dickinson
messages: + msg205955
2011-08-29 16:15:10vinay.sajipsetnosy: + vinay.sajip

messages: + msg143155
versions: + Python 3.3
2011-08-28 18:39:15terry.reedysetnosy: + terry.reedy

messages: + msg143117
title: cast() creates circular reference in original object -> ctypes.cast() creates circular reference in original object
2011-08-24 21:53:54bgilbertcreate