@@ -376,7 +376,7 @@ typedef struct CFieldObject {
376376
377377typedef struct {
378378#ifdef Py_GIL_DISABLED
379- PyMutex mutex ;
379+ PyMutex mutex ; /* critical section mutex */
380380#endif
381381 int initialized ;
382382 Py_ssize_t size ; /* number of bytes */
@@ -405,6 +405,23 @@ typedef struct {
405405/* Py_ssize_t *suboffsets; */ /* unused in ctypes */
406406} StgInfo ;
407407
408+ /*
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+
424+ */
408425
409426#define STGINFO_LOCK (stginfo ) Py_BEGIN_CRITICAL_SECTION_MUT(&(stginfo)->mutex)
410427#define STGINFO_LOCK2 (stginfo1 , stginfo2 ) Py_BEGIN_CRITICAL_SECTION2_MUT(&(stginfo1)->mutex, &(stginfo2)->mutex)
@@ -424,10 +441,12 @@ stginfo_set_dict_final_lock_held(StgInfo *info)
424441 FT_ATOMIC_STORE_INT (info -> dict_final , 1 );
425442}
426443
444+
445+ // Set the `dict_final` bit in StgInfo. It checks if the bit is already set
446+ // and in that avoids acquiring the critical section (general case).
427447static inline void
428448stginfo_set_dict_final (StgInfo * info )
429449{
430- // avoid acquiring the lock if final is already set
431450 if (stginfo_get_dict_final (info ) == 1 ) {
432451 return ;
433452 }
0 commit comments