6
6
7
7
#include "pycore_moduleobject.h" // _PyModule_GetState()
8
8
#include "pycore_typeobject.h" // _PyType_GetModuleState()
9
+ #include "pycore_critical_section.h"
10
+ #include "pycore_pyatomic_ft_wrappers.h"
9
11
10
12
// Do we support C99 complex types in ffi?
11
13
// For Apple's libffi, this must be determined at runtime (see gh-128156).
@@ -375,7 +377,7 @@ typedef struct CFieldObject {
375
377
typedef struct {
376
378
int initialized ;
377
379
Py_ssize_t size ; /* number of bytes */
378
- Py_ssize_t align ; /* alignment requirements */
380
+ Py_ssize_t align ; /* alignment reqwuirements */
379
381
Py_ssize_t length ; /* number of fields */
380
382
ffi_type ffi_type_pointer ;
381
383
PyObject * proto ; /* Only for Pointer/ArrayObject */
@@ -390,15 +392,64 @@ typedef struct {
390
392
PyObject * checker ;
391
393
PyObject * module ;
392
394
int flags ; /* calling convention and such */
395
+ #ifdef Py_GIL_DISABLED
396
+ PyMutex mutex ; /* critical section mutex */
397
+ #endif
398
+ uint8_t dict_final ;
393
399
394
400
/* pep3118 fields, pointers need PyMem_Free */
395
401
char * format ;
396
402
int ndim ;
397
403
Py_ssize_t * shape ;
398
- /* Py_ssize_t *strides; */ /* unused in ctypes */
399
- /* Py_ssize_t *suboffsets; */ /* unused in ctypes */
404
+ /* Py_ssize_t *strides; */ /* unused in ctypes */
405
+ /* Py_ssize_t *suboffsets; */ /* unused in ctypes */
400
406
} StgInfo ;
401
407
408
+
409
+ /*
410
+ To ensure thread safety in the free threading build, the `STGINFO_LOCK` and
411
+ `STGINFO_UNLOCK` macros use critical sections to protect against concurrent
412
+ modifications to `StgInfo` and assignment of the `dict_final` field. Once
413
+ `dict_final` is set, `StgInfo` is treated as read-only, and no further
414
+ modifications are allowed. This approach allows most read operations to
415
+ proceed without acquiring the critical section lock.
416
+
417
+ The `dict_final` field is written only after all other modifications to
418
+ `StgInfo` are complete. The reads and writes of `dict_final` use the
419
+ sequentially consistent memory ordering to ensure that all other fields are
420
+ visible to other threads before the `dict_final` bit is set.
421
+ */
422
+
423
+ #define STGINFO_LOCK (stginfo ) Py_BEGIN_CRITICAL_SECTION_MUT(&(stginfo)->mutex)
424
+ #define STGINFO_UNLOCK () Py_END_CRITICAL_SECTION()
425
+
426
+ static inline uint8_t
427
+ stginfo_get_dict_final (StgInfo * info )
428
+ {
429
+ return FT_ATOMIC_LOAD_UINT8 (info -> dict_final );
430
+ }
431
+
432
+ static inline void
433
+ stginfo_set_dict_final_lock_held (StgInfo * info )
434
+ {
435
+ _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED (& info -> mutex );
436
+ FT_ATOMIC_STORE_UINT8 (info -> dict_final , 1 );
437
+ }
438
+
439
+
440
+ // Set the `dict_final` bit in StgInfo. It checks if the bit is already set
441
+ // and in that avoids acquiring the critical section (general case).
442
+ static inline void
443
+ stginfo_set_dict_final (StgInfo * info )
444
+ {
445
+ if (stginfo_get_dict_final (info ) == 1 ) {
446
+ return ;
447
+ }
448
+ STGINFO_LOCK (info );
449
+ stginfo_set_dict_final_lock_held (info );
450
+ STGINFO_UNLOCK ();
451
+ }
452
+
402
453
extern int PyCStgInfo_clone (StgInfo * dst_info , StgInfo * src_info );
403
454
extern void ctype_clear_stginfo (StgInfo * info );
404
455
@@ -427,8 +478,6 @@ PyObject *_ctypes_callproc(ctypes_state *st,
427
478
#define TYPEFLAG_ISPOINTER 0x100
428
479
#define TYPEFLAG_HASPOINTER 0x200
429
480
430
- #define DICTFLAG_FINAL 0x1000
431
-
432
481
struct tagPyCArgObject {
433
482
PyObject_HEAD
434
483
ffi_type * pffi_type ;
0 commit comments