Skip to content

Commit 176df09

Browse files
authored
gh-112075: Make PyDictKeysObject thread-safe (#114741)
Adds locking for shared PyDictKeysObject's for dictionaries
1 parent 145bc2d commit 176df09

File tree

6 files changed

+257
-93
lines changed

6 files changed

+257
-93
lines changed

Include/cpython/pyatomic.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@ _Py_atomic_load_ptr_acquire(const void *obj);
469469
static inline void
470470
_Py_atomic_store_ptr_release(void *obj, void *value);
471471

472+
static inline void
473+
_Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value);
474+
472475
static inline void
473476
_Py_atomic_store_int_release(int *obj, int value);
474477

@@ -484,6 +487,9 @@ _Py_atomic_load_uint64_acquire(const uint64_t *obj);
484487
static inline uint32_t
485488
_Py_atomic_load_uint32_acquire(const uint32_t *obj);
486489

490+
static inline Py_ssize_t
491+
_Py_atomic_load_ssize_acquire(const Py_ssize_t *obj);
492+
487493

488494
// --- _Py_atomic_fence ------------------------------------------------------
489495

Include/cpython/pyatomic_gcc.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ static inline void
500500
_Py_atomic_store_int_release(int *obj, int value)
501501
{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); }
502502

503+
static inline void
504+
_Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value)
505+
{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); }
506+
503507
static inline int
504508
_Py_atomic_load_int_acquire(const int *obj)
505509
{ return __atomic_load_n(obj, __ATOMIC_ACQUIRE); }
@@ -516,6 +520,10 @@ static inline uint32_t
516520
_Py_atomic_load_uint32_acquire(const uint32_t *obj)
517521
{ return __atomic_load_n(obj, __ATOMIC_ACQUIRE); }
518522

523+
static inline Py_ssize_t
524+
_Py_atomic_load_ssize_acquire(const Py_ssize_t *obj)
525+
{ return __atomic_load_n(obj, __ATOMIC_ACQUIRE); }
526+
519527
// --- _Py_atomic_fence ------------------------------------------------------
520528

521529
static inline void

Include/cpython/pyatomic_msc.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,18 @@ _Py_atomic_store_int_release(int *obj, int value)
939939
#endif
940940
}
941941

942+
static inline void
943+
_Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value)
944+
{
945+
#if defined(_M_X64) || defined(_M_IX86)
946+
*(Py_ssize_t volatile *)obj = value;
947+
#elif defined(_M_ARM64)
948+
__stlr64((unsigned __int64 volatile *)obj, (unsigned __int64)value);
949+
#else
950+
# error "no implementation of _Py_atomic_store_ssize_release"
951+
#endif
952+
}
953+
942954
static inline int
943955
_Py_atomic_load_int_acquire(const int *obj)
944956
{
@@ -990,6 +1002,18 @@ _Py_atomic_load_uint32_acquire(const uint32_t *obj)
9901002
#endif
9911003
}
9921004

1005+
static inline Py_ssize_t
1006+
_Py_atomic_load_ssize_acquire(const Py_ssize_t *obj)
1007+
{
1008+
#if defined(_M_X64) || defined(_M_IX86)
1009+
return *(Py_ssize_t volatile *)obj;
1010+
#elif defined(_M_ARM64)
1011+
return (Py_ssize_t)__ldar64((unsigned __int64 volatile *)obj);
1012+
#else
1013+
# error "no implementation of _Py_atomic_load_ssize_acquire"
1014+
#endif
1015+
}
1016+
9931017
// --- _Py_atomic_fence ------------------------------------------------------
9941018

9951019
static inline void

Include/cpython/pyatomic_std.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,14 @@ _Py_atomic_store_int_release(int *obj, int value)
879879
memory_order_release);
880880
}
881881

882+
static inline void
883+
_Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value)
884+
{
885+
_Py_USING_STD;
886+
atomic_store_explicit((_Atomic(Py_ssize_t)*)obj, value,
887+
memory_order_release);
888+
}
889+
882890
static inline int
883891
_Py_atomic_load_int_acquire(const int *obj)
884892
{
@@ -908,7 +916,13 @@ _Py_atomic_load_uint32_acquire(const uint32_t *obj)
908916
{
909917
_Py_USING_STD;
910918
return atomic_load_explicit((const _Atomic(uint32_t)*)obj,
911-
memory_order_acquire);
919+
}
920+
921+
static inline Py_ssize_t
922+
_Py_atomic_load_ssize_acquire(const Py_ssize_t *obj)
923+
{
924+
_Py_USING_STD;
925+
return atomic_load_explicit((const _Atomic(Py_ssize_t)*)obj,
912926
}
913927

914928

Include/internal/pycore_dict.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ struct _dictkeysobject {
136136
/* Kind of keys */
137137
uint8_t dk_kind;
138138

139+
#ifdef Py_GIL_DISABLED
140+
/* Lock used to protect shared keys */
141+
PyMutex dk_mutex;
142+
#endif
143+
139144
/* Version number -- Reset to 0 by any modification to keys */
140145
uint32_t dk_version;
141146

@@ -145,6 +150,7 @@ struct _dictkeysobject {
145150
/* Number of used entries in dk_entries. */
146151
Py_ssize_t dk_nentries;
147152

153+
148154
/* Actual hash table of dk_size entries. It holds indices in dk_entries,
149155
or DKIX_EMPTY(-1) or DKIX_DUMMY(-2).
150156

0 commit comments

Comments
 (0)