Skip to content

Commit 9db1e17

Browse files
gh-81057: Move the global Dict-Related Versions to _PyRuntimeState (gh-99497)
We also move the global func version. #81057
1 parent 8211cf5 commit 9db1e17

16 files changed

+89
-48
lines changed

Include/internal/pycore_dict.h

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ extern "C" {
99
# error "this header requires Py_BUILD_CORE define"
1010
#endif
1111

12+
#include "pycore_dict_state.h"
13+
#include "pycore_runtime.h" // _PyRuntime
14+
1215

1316
/* runtime lifecycle */
1417

@@ -17,25 +20,6 @@ extern void _PyDict_Fini(PyInterpreterState *interp);
1720

1821
/* other API */
1922

20-
#ifndef WITH_FREELISTS
21-
// without freelists
22-
# define PyDict_MAXFREELIST 0
23-
#endif
24-
25-
#ifndef PyDict_MAXFREELIST
26-
# define PyDict_MAXFREELIST 80
27-
#endif
28-
29-
struct _Py_dict_state {
30-
#if PyDict_MAXFREELIST > 0
31-
/* Dictionary reuse scheme to save calls to malloc and free */
32-
PyDictObject *free_list[PyDict_MAXFREELIST];
33-
int numfree;
34-
PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
35-
int keys_numfree;
36-
#endif
37-
};
38-
3923
typedef struct {
4024
/* Cached hash code of me_key. */
4125
Py_hash_t me_hash;
@@ -152,13 +136,11 @@ struct _dictvalues {
152136
(PyDictUnicodeEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes]))
153137
#define DK_IS_UNICODE(dk) ((dk)->dk_kind != DICT_KEYS_GENERAL)
154138

155-
extern uint64_t _pydict_global_version;
156-
157-
#define DICT_MAX_WATCHERS 8
158139
#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS)
159140
#define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1)
160141

161-
#define DICT_NEXT_VERSION() (_pydict_global_version += DICT_VERSION_INCREMENT)
142+
#define DICT_NEXT_VERSION() \
143+
(_PyRuntime.dict_state.global_version += DICT_VERSION_INCREMENT)
162144

163145
void
164146
_PyDict_SendEvent(int watcher_bits,

Include/internal/pycore_dict_state.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef Py_INTERNAL_DICT_STATE_H
2+
#define Py_INTERNAL_DICT_STATE_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
12+
struct _Py_dict_runtime_state {
13+
/*Global counter used to set ma_version_tag field of dictionary.
14+
* It is incremented each time that a dictionary is created and each
15+
* time that a dictionary is modified. */
16+
uint64_t global_version;
17+
uint32_t next_keys_version;
18+
};
19+
20+
21+
#ifndef WITH_FREELISTS
22+
// without freelists
23+
# define PyDict_MAXFREELIST 0
24+
#endif
25+
26+
#ifndef PyDict_MAXFREELIST
27+
# define PyDict_MAXFREELIST 80
28+
#endif
29+
30+
#define DICT_MAX_WATCHERS 8
31+
32+
struct _Py_dict_state {
33+
#if PyDict_MAXFREELIST > 0
34+
/* Dictionary reuse scheme to save calls to malloc and free */
35+
PyDictObject *free_list[PyDict_MAXFREELIST];
36+
PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
37+
int numfree;
38+
int keys_numfree;
39+
#endif
40+
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
41+
};
42+
43+
44+
#ifdef __cplusplus
45+
}
46+
#endif
47+
#endif /* !Py_INTERNAL_DICT_STATE_H */

Include/internal/pycore_function.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
struct _py_func_runtime_state {
12+
uint32_t next_version;
13+
};
14+
1115
extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor *constr);
1216

1317
extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);

Include/internal/pycore_interp.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extern "C" {
1414
#include "pycore_ast_state.h" // struct ast_state
1515
#include "pycore_code.h" // struct callable_cache
1616
#include "pycore_context.h" // struct _Py_context_state
17-
#include "pycore_dict.h" // struct _Py_dict_state
17+
#include "pycore_dict_state.h" // struct _Py_dict_state
1818
#include "pycore_exceptions.h" // struct _Py_exc_state
1919
#include "pycore_floatobject.h" // struct _Py_float_state
2020
#include "pycore_genobject.h" // struct _Py_async_gen_state
@@ -171,8 +171,6 @@ struct _is {
171171
// Initialized to _PyEval_EvalFrameDefault().
172172
_PyFrameEvalFunction eval_frame;
173173

174-
PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS];
175-
176174
Py_ssize_t co_extra_user_count;
177175
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
178176

Include/internal/pycore_runtime.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ extern "C" {
99
#endif
1010

1111
#include "pycore_atomic.h" /* _Py_atomic_address */
12+
#include "pycore_dict_state.h" // struct _Py_dict_runtime_state
1213
#include "pycore_dtoa.h" // struct _dtoa_runtime_state
1314
#include "pycore_floatobject.h" // struct _Py_float_runtime_state
15+
#include "pycore_function.h" // struct _func_runtime_state
1416
#include "pycore_gil.h" // struct _gil_runtime_state
1517
#include "pycore_global_objects.h" // struct _Py_global_objects
1618
#include "pycore_import.h" // struct _import_runtime_state
@@ -151,6 +153,8 @@ typedef struct pyruntimestate {
151153

152154
struct _Py_float_runtime_state float_state;
153155
struct _Py_unicode_runtime_state unicode_state;
156+
struct _Py_dict_runtime_state dict_state;
157+
struct _py_func_runtime_state func_state;
154158

155159
struct {
156160
/* Used to set PyTypeObject.tp_version_tag */

Include/internal/pycore_runtime_init.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ extern "C" {
5858
.float_format = _py_float_format_unknown, \
5959
.double_format = _py_float_format_unknown, \
6060
}, \
61+
.dict_state = { \
62+
.next_keys_version = 2, \
63+
}, \
64+
.func_state = { \
65+
.next_version = 1, \
66+
}, \
6167
.types = { \
6268
.next_version_tag = 1, \
6369
}, \

Makefile.pre.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,7 @@ PYTHON_HEADERS= \
16271627
$(srcdir)/Include/internal/pycore_condvar.h \
16281628
$(srcdir)/Include/internal/pycore_context.h \
16291629
$(srcdir)/Include/internal/pycore_dict.h \
1630+
$(srcdir)/Include/internal/pycore_dict_state.h \
16301631
$(srcdir)/Include/internal/pycore_descrobject.h \
16311632
$(srcdir)/Include/internal/pycore_dtoa.h \
16321633
$(srcdir)/Include/internal/pycore_exceptions.h \

Modules/_functoolsmodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Python.h"
22
#include "pycore_call.h" // _PyObject_CallNoArgs()
3+
#include "pycore_dict.h" // _PyDict_Pop_KnownHash()
34
#include "pycore_long.h" // _PyLong_GetZero()
45
#include "pycore_moduleobject.h" // _PyModule_GetState()
56
#include "pycore_object.h" // _PyObject_GC_TRACK

Objects/call.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "Python.h"
22
#include "pycore_call.h" // _PyObject_CallNoArgsTstate()
33
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
4+
#include "pycore_dict.h" // _PyDict_FromItems()
45
#include "pycore_object.h" // _PyCFunctionWithKeywords_TrampolineCall()
56
#include "pycore_pyerrors.h" // _PyErr_Occurred()
67
#include "pycore_pystate.h" // _PyThreadState_GET()

Objects/dictobject.c

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,6 @@ static int dictresize(PyDictObject *mp, uint8_t log_newsize, int unicode);
236236

237237
static PyObject* dict_iter(PyDictObject *dict);
238238

239-
/*Global counter used to set ma_version_tag field of dictionary.
240-
* It is incremented each time that a dictionary is created and each
241-
* time that a dictionary is modified. */
242-
uint64_t _pydict_global_version = 0;
243-
244239
#include "clinic/dictobject.c.h"
245240

246241

@@ -5658,17 +5653,15 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys)
56585653
dictkeys_decref(keys);
56595654
}
56605655

5661-
static uint32_t next_dict_keys_version = 2;
5662-
56635656
uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictKeysObject *dictkeys)
56645657
{
56655658
if (dictkeys->dk_version != 0) {
56665659
return dictkeys->dk_version;
56675660
}
5668-
if (next_dict_keys_version == 0) {
5661+
if (_PyRuntime.dict_state.next_keys_version == 0) {
56695662
return 0;
56705663
}
5671-
uint32_t v = next_dict_keys_version++;
5664+
uint32_t v = _PyRuntime.dict_state.next_keys_version++;
56725665
dictkeys->dk_version = v;
56735666
return v;
56745667
}
@@ -5680,7 +5673,7 @@ validate_watcher_id(PyInterpreterState *interp, int watcher_id)
56805673
PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id);
56815674
return -1;
56825675
}
5683-
if (!interp->dict_watchers[watcher_id]) {
5676+
if (!interp->dict_state.watchers[watcher_id]) {
56845677
PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id);
56855678
return -1;
56865679
}
@@ -5723,8 +5716,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback)
57235716
PyInterpreterState *interp = _PyInterpreterState_GET();
57245717

57255718
for (int i = 0; i < DICT_MAX_WATCHERS; i++) {
5726-
if (!interp->dict_watchers[i]) {
5727-
interp->dict_watchers[i] = callback;
5719+
if (!interp->dict_state.watchers[i]) {
5720+
interp->dict_state.watchers[i] = callback;
57285721
return i;
57295722
}
57305723
}
@@ -5740,7 +5733,7 @@ PyDict_ClearWatcher(int watcher_id)
57405733
if (validate_watcher_id(interp, watcher_id)) {
57415734
return -1;
57425735
}
5743-
interp->dict_watchers[watcher_id] = NULL;
5736+
interp->dict_state.watchers[watcher_id] = NULL;
57445737
return 0;
57455738
}
57465739

@@ -5754,7 +5747,7 @@ _PyDict_SendEvent(int watcher_bits,
57545747
PyInterpreterState *interp = _PyInterpreterState_GET();
57555748
for (int i = 0; i < DICT_MAX_WATCHERS; i++) {
57565749
if (watcher_bits & 1) {
5757-
PyDict_WatchCallback cb = interp->dict_watchers[i];
5750+
PyDict_WatchCallback cb = interp->dict_state.watchers[i];
57585751
if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) {
57595752
// some dict modification paths (e.g. PyDict_Clear) can't raise, so we
57605753
// can't propagate exceptions from dict watchers.

Objects/funcobject.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
#include "pycore_pyerrors.h" // _PyErr_Occurred()
88
#include "structmember.h" // PyMemberDef
99

10-
static uint32_t next_func_version = 1;
11-
1210
PyFunctionObject *
1311
_PyFunction_FromConstructor(PyFrameConstructor *constr)
1412
{
@@ -128,10 +126,10 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
128126
if (func->vectorcall != _PyFunction_Vectorcall) {
129127
return 0;
130128
}
131-
if (next_func_version == 0) {
129+
if (_PyRuntime.func_state.next_version == 0) {
132130
return 0;
133131
}
134-
uint32_t v = next_func_version++;
132+
uint32_t v = _PyRuntime.func_state.next_version++;
135133
func->func_version = v;
136134
return v;
137135
}

Objects/typeobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "pycore_call.h"
55
#include "pycore_code.h" // CO_FAST_FREE
66
#include "pycore_compile.h" // _Py_Mangle()
7+
#include "pycore_dict.h" // _PyDict_KeysSize()
78
#include "pycore_initconfig.h" // _PyStatus_OK()
89
#include "pycore_moduleobject.h" // _PyModule_GetDef()
910
#include "pycore_object.h" // _PyType_HasFeature()

PCbuild/pythoncore.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@
208208
<ClInclude Include="..\Include\internal\pycore_condvar.h" />
209209
<ClInclude Include="..\Include\internal\pycore_context.h" />
210210
<ClInclude Include="..\Include\internal\pycore_descrobject.h" />
211+
<ClInclude Include="..\Include\internal\pycore_dict.h" />
212+
<ClInclude Include="..\Include\internal\pycore_dict_state.h" />
211213
<ClInclude Include="..\Include\internal\pycore_dtoa.h" />
212214
<ClInclude Include="..\Include\internal\pycore_exceptions.h" />
213215
<ClInclude Include="..\Include\internal\pycore_fileutils.h" />

PCbuild/pythoncore.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,12 @@
531531
<ClInclude Include="..\Include\internal\pycore_descrobject.h">
532532
<Filter>Include\internal</Filter>
533533
</ClInclude>
534+
<ClInclude Include="..\Include\internal\pycore_dict.h">
535+
<Filter>Include\internal</Filter>
536+
</ClInclude>
537+
<ClInclude Include="..\Include\internal\pycore_dict_state.h">
538+
<Filter>Include\internal</Filter>
539+
</ClInclude>
534540
<ClInclude Include="..\Include\internal\pycore_dtoa.h">
535541
<Filter>Include\internal</Filter>
536542
</ClInclude>

Python/pystate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
458458
Py_CLEAR(interp->interpreter_trampoline);
459459

460460
for (int i=0; i < DICT_MAX_WATCHERS; i++) {
461-
interp->dict_watchers[i] = NULL;
461+
interp->dict_state.watchers[i] = NULL;
462462
}
463463

464464
// XXX Once we have one allocator per interpreter (i.e.

Tools/c-analyzer/cpython/globals-to-fix.tsv

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,6 @@ Objects/unicodeobject.c - ucnhash_capi -
320320
Python/suggestions.c levenshtein_distance buffer -
321321

322322
# other
323-
Objects/dictobject.c - _pydict_global_version -
324-
Objects/dictobject.c - next_dict_keys_version -
325-
Objects/funcobject.c - next_func_version -
326323
Objects/object.c - _Py_RefTotal -
327324
Python/perf_trampoline.c - perf_status -
328325
Python/perf_trampoline.c - extra_code_index -

0 commit comments

Comments
 (0)