struct _Py_unicode_fs_codec fs_codec;
};
+struct _Py_float_state {
+ /* Special free list
+ free_list is a singly-linked list of available PyFloatObjects,
+ linked via abuse of their ob_type members. */
+ int numfree;
+ PyFloatObject *free_list;
+};
+
/* Speed optimization to avoid frequent malloc/free of small tuples */
#ifndef PyTuple_MAXSAVESIZE
// Largest tuple to save on free list
int numfree;
};
-struct _Py_float_state {
- /* Special free list
- free_list is a singly-linked list of available PyFloatObjects,
- linked via abuse of their ob_type members. */
+#ifndef PyDict_MAXFREELIST
+# define PyDict_MAXFREELIST 80
+#endif
+
+struct _Py_dict_state {
+ /* Dictionary reuse scheme to save calls to malloc and free */
+ PyDictObject *free_list[PyDict_MAXFREELIST];
int numfree;
- PyFloatObject *free_list;
+ PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
+ int keys_numfree;
};
struct _Py_frame_state {
};
-
/* interpreter state */
#define _PY_NSMALLPOSINTS 257
PyObject *codec_error_registry;
int codecs_initialized;
- struct _Py_unicode_state unicode;
-
PyConfig config;
#ifdef HAVE_DLOPEN
int dlopenflags;
*/
PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
#endif
+ struct _Py_unicode_state unicode;
+ struct _Py_float_state float_state;
+ /* Using a cache is very effective since typically only a single slice is
+ created and then deleted again. */
+ PySliceObject *slice_cache;
+
struct _Py_tuple_state tuple;
struct _Py_list_state list;
- struct _Py_float_state float_state;
+ struct _Py_dict_state dict_state;
struct _Py_frame_state frame;
struct _Py_async_gen_state async_gen;
struct _Py_context_state context;
-
- /* Using a cache is very effective since typically only a single slice is
- created and then deleted again. */
- PySliceObject *slice_cache;
};
/* Used by _PyImport_Cleanup() */
#define DICT_NEXT_VERSION() (++pydict_global_version)
-/* Dictionary reuse scheme to save calls to malloc and free */
-#ifndef PyDict_MAXFREELIST
-#define PyDict_MAXFREELIST 80
-#endif
-
-/* bpo-40521: dict free lists are shared by all interpreters. */
-#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
-# undef PyDict_MAXFREELIST
-# define PyDict_MAXFREELIST 0
-#endif
-
-#if PyDict_MAXFREELIST > 0
-static PyDictObject *free_list[PyDict_MAXFREELIST];
-static int numfree = 0;
-static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
-static int numfreekeys = 0;
-#endif
-
#include "clinic/dictobject.c.h"
void
-_PyDict_ClearFreeList(void)
+_PyDict_ClearFreeList(PyThreadState *tstate)
{
-#if PyDict_MAXFREELIST > 0
- while (numfree) {
- PyDictObject *op = free_list[--numfree];
+ struct _Py_dict_state *state = &tstate->interp->dict_state;
+ while (state->numfree) {
+ PyDictObject *op = state->free_list[--state->numfree];
assert(PyDict_CheckExact(op));
PyObject_GC_Del(op);
}
- while (numfreekeys) {
- PyObject_FREE(keys_free_list[--numfreekeys]);
+ while (state->keys_numfree) {
+ PyObject_FREE(state->keys_free_list[--state->keys_numfree]);
}
-#endif
}
-/* Print summary info about the state of the optimized allocator */
+
void
-_PyDict_DebugMallocStats(FILE *out)
+_PyDict_Fini(PyThreadState *tstate)
{
-#if PyDict_MAXFREELIST > 0
- _PyDebugAllocatorStats(out,
- "free PyDictObject", numfree, sizeof(PyDictObject));
+ _PyDict_ClearFreeList(tstate);
+#ifdef Py_DEBUG
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+ state->numfree = -1;
+ state->keys_numfree = -1;
#endif
}
+/* Print summary info about the state of the optimized allocator */
void
-_PyDict_Fini(void)
+_PyDict_DebugMallocStats(FILE *out)
{
- _PyDict_ClearFreeList();
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+ _PyDebugAllocatorStats(out, "free PyDictObject",
+ state->numfree, sizeof(PyDictObject));
}
+
#define DK_SIZE(dk) ((dk)->dk_size)
#if SIZEOF_VOID_P > 4
#define DK_IXSIZE(dk) \
}
-static PyDictKeysObject *new_keys_object(Py_ssize_t size)
+static PyDictKeysObject*
+new_keys_object(Py_ssize_t size)
{
PyDictKeysObject *dk;
Py_ssize_t es, usable;
es = sizeof(Py_ssize_t);
}
-#if PyDict_MAXFREELIST > 0
- if (size == PyDict_MINSIZE && numfreekeys > 0) {
- dk = keys_free_list[--numfreekeys];
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+#ifdef Py_DEBUG
+ // new_keys_object() must not be called after _PyDict_Fini()
+ assert(state->keys_numfree != -1);
+#endif
+ if (size == PyDict_MINSIZE && state->keys_numfree > 0) {
+ dk = state->keys_free_list[--state->keys_numfree];
}
else
-#endif
{
dk = PyObject_MALLOC(sizeof(PyDictKeysObject)
+ es * size
Py_XDECREF(entries[i].me_key);
Py_XDECREF(entries[i].me_value);
}
-#if PyDict_MAXFREELIST > 0
- if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) {
- keys_free_list[numfreekeys++] = keys;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+#ifdef Py_DEBUG
+ // free_keys_object() must not be called after _PyDict_Fini()
+ assert(state->keys_numfree != -1);
+#endif
+ if (keys->dk_size == PyDict_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST) {
+ state->keys_free_list[state->keys_numfree++] = keys;
return;
}
-#endif
PyObject_FREE(keys);
}
{
PyDictObject *mp;
assert(keys != NULL);
-#if PyDict_MAXFREELIST > 0
- if (numfree) {
- mp = free_list[--numfree];
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+#ifdef Py_DEBUG
+ // new_dict() must not be called after _PyDict_Fini()
+ assert(state->numfree != -1);
+#endif
+ if (state->numfree) {
+ mp = state->free_list[--state->numfree];
assert (mp != NULL);
assert (Py_IS_TYPE(mp, &PyDict_Type));
_Py_NewReference((PyObject *)mp);
}
- else
-#endif
- {
+ else {
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
if (mp == NULL) {
dictkeys_decref(keys);
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
-#if PyDict_MAXFREELIST > 0
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+#ifdef Py_DEBUG
+ // dictresize() must not be called after _PyDict_Fini()
+ assert(state->keys_numfree != -1);
+#endif
if (oldkeys->dk_size == PyDict_MINSIZE &&
- numfreekeys < PyDict_MAXFREELIST)
+ state->keys_numfree < PyDict_MAXFREELIST)
{
- keys_free_list[numfreekeys++] = oldkeys;
+ state->keys_free_list[state->keys_numfree++] = oldkeys;
}
- else
-#endif
- {
+ else {
PyObject_FREE(oldkeys);
}
}
assert(keys->dk_refcnt == 1);
dictkeys_decref(keys);
}
-#if PyDict_MAXFREELIST > 0
- if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
- free_list[numfree++] = mp;
- }
- else
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _Py_dict_state *state = &interp->dict_state;
+#ifdef Py_DEBUG
+ // new_dict() must not be called after _PyDict_Fini()
+ assert(state->numfree != -1);
#endif
- {
+ if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
+ state->free_list[state->numfree++] = mp;
+ }
+ else {
Py_TYPE(mp)->tp_free((PyObject *)mp);
}
Py_TRASHCAN_END