Skip to content

Commit ef9ba7a

Browse files
code review
1 parent bfd7c21 commit ef9ba7a

File tree

4 files changed

+13
-115
lines changed

4 files changed

+13
-115
lines changed

Lib/test/test_ctypes/test_free_threading.py

Lines changed: 0 additions & 89 deletions
This file was deleted.

Modules/_ctypes/_ctypes.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -716,9 +716,6 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc
716716
/* copy base info */
717717
ret = PyCStgInfo_clone(info, baseinfo);
718718
if (ret >= 0) {
719-
// clear the 'final' bit in the subclass info
720-
// safe to modify without atomics as it is not exposed to other threads
721-
info->dict_final = 0;
722719
stginfo_set_dict_final_lock_held(baseinfo); /* set the 'final' flag in the baseclass info */
723720
}
724721
STGINFO_UNLOCK();

Modules/_ctypes/ctypes.h

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -406,21 +406,17 @@ typedef struct {
406406
} StgInfo;
407407

408408
/*
409-
In free-threading, concurrent mutations to StgInfo is not thread safe.
410-
Therefore to make it thread safe, when modifying StgInfo, `STGINFO_LOCK` and
411-
`STGINFO_UNLOCK` macros are used to acquire critical section of the StgInfo.
412-
The critical section is write only and is acquired when modifying the
413-
StgInfo fields and while setting the `dict_final` bit. Once the `dict_final`
414-
is set, StgInfo is treated as read only and no further modifications are
415-
allowed. This allows to avoid acquiring the critical section for most
416-
read operations when `dict_final` is set (general case).
417-
418-
It is important to set all the fields before setting the `dict_final` bit
419-
in functions like `PyCStructUnionType_update_stginfo` because the store of
420-
`dict_final` uses sequential consistency memory ordering. This ensures that
421-
all the other fields are visible to other threads before the `dict_final` bit
422-
is set thus allowing for lock free reads when `dict_final` is set.
423-
409+
To ensure thread safety in the free threading build, the `STGINFO_LOCK` and
410+
`STGINFO_UNLOCK` macros use critical sections to protect against concurrent
411+
modifications to `StgInfo` and assignment of the `dict_final` field. Once
412+
`dict_final` is set, `StgInfo` is treated as read-only, and no further
413+
modifications are allowed. This approach allows most read operations to
414+
proceed without acquiring the critical section lock.
415+
416+
The `dict_final` field is written only after all other modifications to
417+
`StgInfo` are complete. The reads and writes of `dict_final` use the
418+
sequentially consistent memory ordering to ensure that all other fields are
419+
visible to other threads before the `dict_final` bit is set.
424420
*/
425421

426422
#define STGINFO_LOCK(stginfo) Py_BEGIN_CRITICAL_SECTION_MUT(&(stginfo)->mutex)

Modules/_ctypes/stgdict.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info)
3737
#ifdef Py_GIL_DISABLED
3838
dst_info->mutex = (PyMutex){0};
3939
#endif
40+
dst_info->dict_final = 0;
4041

4142
Py_XINCREF(dst_info->proto);
4243
Py_XINCREF(dst_info->argtypes);
@@ -264,16 +265,9 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
264265
return -1;
265266
}
266267

268+
STGINFO_LOCK(stginfo);
267269
/* If this structure/union is already marked final we cannot assign
268270
_fields_ anymore. */
269-
270-
if (stginfo_get_dict_final(stginfo) == 1) {/* is final ? */
271-
PyErr_SetString(PyExc_AttributeError,
272-
"_fields_ is final");
273-
return -1;
274-
}
275-
STGINFO_LOCK(stginfo);
276-
// check again after locking
277271
if (stginfo_get_dict_final(stginfo) == 1) {/* is final ? */
278272
PyErr_SetString(PyExc_AttributeError,
279273
"_fields_ is final");

0 commit comments

Comments
 (0)