Skip to content

Commit 460ae61

Browse files
Roll back the global singleton part.
1 parent f4acc36 commit 460ae61

File tree

8 files changed

+138
-79
lines changed

8 files changed

+138
-79
lines changed

Include/internal/pycore_gc.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ typedef struct {
2020
} PyGC_Head;
2121

2222
#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
23-
#define _PyGC_Head_UNUSED PyGC_Head
2423

2524
/* True if the object is currently tracked by the GC. */
2625
#define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0)

Include/internal/pycore_global_objects.h

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

11-
#include "pycore_gc.h" // PyGC_Head
1211
#include "pycore_global_strings.h" // struct _Py_global_strings
1312

1413

@@ -41,9 +40,6 @@ struct _Py_global_objects {
4140
} bytes_characters[256];
4241

4342
struct _Py_global_strings strings;
44-
45-
_PyGC_Head_UNUSED _tuple_empty_gc_not_used;
46-
PyTupleObject tuple_empty;
4743
} singletons;
4844
};
4945

Include/internal/pycore_runtime_init.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -964,10 +964,6 @@ extern "C" {
964964
INIT_ID(zipimporter), \
965965
}, \
966966
}, \
967-
\
968-
.tuple_empty = { \
969-
.ob_base = _PyVarObject_IMMORTAL_INIT(&PyTuple_Type, 0) \
970-
}, \
971967
}, \
972968
}
973969
/* End auto-generated code */

Include/internal/pycore_tuple.h

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,54 @@ extern "C" {
1313

1414
/* runtime lifecycle */
1515

16+
extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);
1617
extern PyStatus _PyTuple_InitTypes(PyInterpreterState *);
1718
extern void _PyTuple_Fini(PyInterpreterState *);
1819

1920

2021
/* other API */
2122

22-
#ifndef WITH_FREELISTS
23-
// without freelists
24-
// for tuples only store empty tuple singleton
25-
# define PyTuple_MAXSAVESIZE 0
26-
# define PyTuple_MAXFREELIST 0
27-
#endif
23+
// PyTuple_MAXSAVESIZE - largest tuple to save on free list
24+
// PyTuple_MAXFREELIST - maximum number of tuples of each size to save
2825

29-
/* Speed optimization to avoid frequent malloc/free of small tuples */
30-
#ifndef PyTuple_MAXSAVESIZE
31-
// Largest tuple to save on free list
32-
# define PyTuple_MAXSAVESIZE 20
33-
#endif
34-
#ifndef PyTuple_MAXFREELIST
35-
// Maximum number of tuples of each size to save
36-
# define PyTuple_MAXFREELIST 2000
26+
#if PyTuple_MAXSAVESIZE <= 0
27+
// A build indicated that no tuple freelists should be used.
28+
# define PyTuple_NFREELISTS 0
29+
# undef PyTuple_MAXSAVESIZE
30+
31+
#elif !defined(WITH_FREELISTS)
32+
// Only store the empty tuple singleton.
33+
# define PyTuple_NFREELISTS 1
34+
# ifndef PyTuple_MAXSAVESIZE
35+
# define PyTuple_MAXSAVESIZE 0
36+
# endif
37+
# ifndef PyTuple_MAXFREELIST
38+
# define PyTuple_MAXFREELIST 1
39+
# endif
40+
41+
#else
42+
# ifndef PyTuple_MAXSAVESIZE
43+
# define PyTuple_MAXSAVESIZE 20
44+
# endif
45+
# define PyTuple_NFREELISTS (PyTuple_MAXSAVESIZE + 1)
46+
# ifndef PyTuple_MAXFREELIST
47+
# define PyTuple_MAXFREELIST 2000
48+
# endif
3749
#endif
3850

3951
struct _Py_tuple_state {
40-
#if PyTuple_MAXSAVESIZE > 0
52+
#if PyTuple_NFREELISTS > 0
4153
/* There is one freelist for each size from 1 to PyTuple_MAXSAVESIZE.
42-
The empty tuple is handled separately.
54+
Entry 0 is the empty tuple () of which at most one instance
55+
will be allocated.
4356
4457
Each tuple stored in the array is the head of the linked list
4558
(and the next available tuple) for that size. The actual tuple
4659
object is used as the linked list node, with its first item
4760
(ob_item[0]) pointing to the next node (i.e. the previous head).
4861
Each linked list is initially NULL. */
49-
PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
50-
int numfree[PyTuple_MAXSAVESIZE];
62+
PyTupleObject *free_list[PyTuple_NFREELISTS];
63+
int numfree[PyTuple_NFREELISTS];
5164
#endif
5265
};
5366

Objects/tupleobject.c

Lines changed: 102 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ static inline int maybe_freelist_push(PyTupleObject *);
3333
static PyTupleObject *
3434
tuple_alloc(Py_ssize_t size)
3535
{
36-
assert(size != 0); // The empty tuple is statically allocated.
3736
if (size < 0) {
3837
PyErr_BadInternalCall();
3938
return NULL;
@@ -53,13 +52,23 @@ tuple_alloc(Py_ssize_t size)
5352
return op;
5453
}
5554

55+
static inline PyTupleObject *maybe_freelist_get_empty_singleton(void);
56+
5657
static inline PyObject *
5758
tuple_get_empty(void)
5859
{
59-
Py_INCREF(&_Py_SINGLETON(tuple_empty));
60-
return (PyObject *)&_Py_SINGLETON(tuple_empty);
60+
PyTupleObject *op = maybe_freelist_get_empty_singleton();
61+
if (op != NULL) {
62+
Py_INCREF(op);
63+
}
64+
else {
65+
op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, 0);
66+
_PyObject_GC_TRACK(op);
67+
}
68+
return (PyObject *)op;
6169
}
6270

71+
// Note that tuple subclasses have their own empty instances.
6372

6473
PyObject *
6574
PyTuple_New(Py_ssize_t size)
@@ -181,26 +190,14 @@ PyTuple_Pack(Py_ssize_t n, ...)
181190
static void
182191
tupledealloc(PyTupleObject *op)
183192
{
184-
if (Py_SIZE(op) == 0) {
185-
/* The empty tuple is statically allocated. */
186-
if (op == &_Py_SINGLETON(tuple_empty)) {
187-
#ifdef Py_DEBUG
188-
_Py_FatalRefcountError("deallocating the empty tuple singleton");
189-
#else
190-
return;
191-
#endif
192-
}
193-
/* tuple subclasses have their own empty instances. */
194-
assert(!PyTuple_CheckExact(op));
195-
}
196-
197193
PyObject_GC_UnTrack(op);
198194
Py_TRASHCAN_BEGIN(op, tupledealloc)
199195

200196
Py_ssize_t i = Py_SIZE(op);
201197
while (--i >= 0) {
202198
Py_XDECREF(op->ob_item[i]);
203199
}
200+
// This will abort on the empty singleton (if there is one).
204201
if (!maybe_freelist_push(op)) {
205202
Py_TYPE(op)->tp_free((PyObject *)op);
206203
}
@@ -931,9 +928,6 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
931928
return 0;
932929
}
933930
if (oldsize == 0) {
934-
#ifdef Py_DEBUG
935-
assert(v == &_Py_SINGLETON(tuple_empty));
936-
#endif
937931
/* The empty tuple is statically allocated so we never
938932
resize it in-place. */
939933
Py_DECREF(v);
@@ -990,20 +984,29 @@ _PyTuple_InitTypes(PyInterpreterState *interp)
990984
return _PyStatus_OK();
991985
}
992986

993-
static void maybe_freelist_clear(PyInterpreterState *);
994-
static void maybe_freelist_fini(PyInterpreterState *);
987+
static int maybe_freelist_init_empty_tuple(PyInterpreterState *);
988+
989+
PyStatus
990+
_PyTuple_InitGlobalObjects(PyInterpreterState *interp)
991+
{
992+
if (maybe_freelist_init_empty_tuple(interp) < 0) {
993+
return _PyStatus_NO_MEMORY();
994+
}
995+
return _PyStatus_OK();
996+
}
997+
998+
static void maybe_freelist_clear(PyInterpreterState *, int);
995999

9961000
void
9971001
_PyTuple_Fini(PyInterpreterState *interp)
9981002
{
999-
maybe_freelist_clear(interp);
1000-
maybe_freelist_fini(interp);
1003+
maybe_freelist_clear(interp, 1);
10011004
}
10021005

10031006
void
10041007
_PyTuple_ClearFreeList(PyInterpreterState *interp)
10051008
{
1006-
maybe_freelist_clear(interp);
1009+
maybe_freelist_clear(interp, 0);
10071010
}
10081011

10091012
/*********************** Tuple Iterator **************************/
@@ -1160,17 +1163,56 @@ tuple_iter(PyObject *seq)
11601163
#define STATE (interp->tuple)
11611164
#define FREELIST_FINALIZED (STATE.numfree[0] < 0)
11621165

1166+
static int
1167+
maybe_freelist_init_empty_tuple(PyInterpreterState *interp)
1168+
{
1169+
#if PyTuple_NFREELISTS > 0
1170+
assert(STATE.free_list[0] == NULL);
1171+
1172+
PyTupleObject *op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, 0);
1173+
if (op == NULL) {
1174+
return -1;
1175+
}
1176+
// The empty tuple singleton is not tracked by the GC.
1177+
// It does not contain any Python object.
1178+
1179+
STATE.free_list[0] = op;
1180+
assert(STATE.numfree[0] == 0);
1181+
STATE.numfree[0] = 1;
1182+
#endif
1183+
return 0;
1184+
}
1185+
1186+
static inline PyTupleObject *
1187+
maybe_freelist_get_empty_singleton(void)
1188+
{
1189+
#if PyTuple_NFREELISTS > 0
1190+
PyTupleObject *op = STATE.free_list[0];
1191+
// maybe_freelist_get_empty_singleton() must not be called
1192+
// before maybe_freelist_init_empty_tuple()
1193+
// or after maybe_freelist_clear(fini=1).
1194+
assert(op != NULL);
1195+
#ifdef Py_DEBUG
1196+
assert(STATE.numfree[0] == 1);
1197+
#endif
1198+
return (PyObject *) op;
1199+
#else
1200+
return NULL;
1201+
#endif
1202+
}
1203+
11631204
static inline PyTupleObject *
11641205
maybe_freelist_pop(Py_ssize_t size)
11651206
{
1166-
#if PyTuple_MAXSAVESIZE > 0
1207+
#if PyTuple_NFREELISTS > 0
11671208
PyInterpreterState *interp = _PyInterpreterState_GET();
11681209
#ifdef Py_DEBUG
11691210
/* maybe_freelist_pop() must not be called after maybe_freelist_fini(). */
11701211
assert(!FREELIST_FINALIZED);
11711212
#endif
1172-
Py_ssize_t index = size - 1;
1173-
if (index < PyTuple_MAXSAVESIZE) {
1213+
assert(size > 0);
1214+
if (size < PyTuple_MAXSAVESIZE) {
1215+
Py_ssize_t index = size;
11741216
PyTupleObject *op = STATE.free_list[index];
11751217
if (op != NULL) {
11761218
/* op is the head of a linked list, with the first item
@@ -1196,14 +1238,24 @@ maybe_freelist_pop(Py_ssize_t size)
11961238
static inline int
11971239
maybe_freelist_push(PyTupleObject *op)
11981240
{
1199-
#if PyTuple_MAXSAVESIZE > 0
1241+
#if PyTuple_NFREELISTS > 0
12001242
PyInterpreterState *interp = _PyInterpreterState_GET();
12011243
#ifdef Py_DEBUG
12021244
/* maybe_freelist_push() must not be called after maybe_freelist_fini(). */
12031245
assert(!FREELIST_FINALIZED);
12041246
#endif
1205-
Py_ssize_t index = Py_SIZE(op) - 1;
1206-
if (index < PyTuple_MAXSAVESIZE
1247+
if (Py_SIZE(op) == 0) {
1248+
#ifdef Py_DEBUG
1249+
// The empty tuple singleton must only be deallocated by
1250+
// maybe_freelist_fini(): not before, not after.
1251+
if (op == STATE.free_list[0] && STATE.numfree[0] < 0) {
1252+
_Py_FatalRefcountError("deallocating the empty tuple singleton");
1253+
}
1254+
#endif
1255+
return 1;
1256+
}
1257+
Py_ssize_t index = Py_SIZE(op);
1258+
if (index < PyTuple_NFREELISTS
12071259
&& STATE.numfree[index] < PyTuple_MAXFREELIST
12081260
&& Py_IS_TYPE(op, &PyTuple_Type))
12091261
{
@@ -1219,13 +1271,26 @@ maybe_freelist_push(PyTupleObject *op)
12191271
}
12201272

12211273
static void
1222-
maybe_freelist_clear(PyInterpreterState *interp)
1274+
maybe_freelist_clear(PyInterpreterState *interp, int fini)
12231275
{
1224-
#if PyTuple_MAXSAVESIZE > 0
1225-
for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1276+
#if PyTuple_NFREELISTS > 0
1277+
// The empty tuple singleton is only cleared during finalization.
1278+
if (fini) {
1279+
assert(!_PyObject_GC_IS_TRACKED(STATE.free_list[0]));
1280+
// XXX Is this right?
1281+
assert(STATE.free_list[0].ob_item[0] == NULL);
1282+
#ifdef Py_DEBUG
1283+
STATE.numfree[0] = 0;
1284+
#endif
1285+
Py_CLEAR(STATE.free_list[0]);
1286+
#ifdef Py_DEBUG
1287+
STATE.numfree[0] = -1;
1288+
#endif
1289+
}
1290+
for (Py_ssize_t i = 1; i < PyTuple_NFREELISTS; i++) {
12261291
PyTupleObject *p = STATE.free_list[i];
12271292
STATE.free_list[i] = NULL;
1228-
STATE.numfree[i] = 0;
1293+
STATE.numfree[i] = fini ? -1 : 0;
12291294
while (p) {
12301295
PyTupleObject *q = p;
12311296
p = (PyTupleObject *)(p->ob_item[0]);
@@ -1235,24 +1300,14 @@ maybe_freelist_clear(PyInterpreterState *interp)
12351300
#endif
12361301
}
12371302

1238-
static void
1239-
maybe_freelist_fini(PyInterpreterState *interp)
1240-
{
1241-
#if PyTuple_MAXSAVESIZE > 0
1242-
for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1243-
STATE.numfree[i] = -1;
1244-
}
1245-
#endif
1246-
}
1247-
12481303
/* Print summary info about the state of the optimized allocator */
12491304
void
12501305
_PyTuple_DebugMallocStats(FILE *out)
12511306
{
1252-
#if PyTuple_MAXSAVESIZE > 0
1307+
#if PyTuple_NFREELISTS > 0
12531308
PyInterpreterState *interp = _PyInterpreterState_GET();
1254-
for (int i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1255-
int len = i + 1;
1309+
for (int i = 0; i < PyTuple_NFREELISTS; i++) {
1310+
int len = i;
12561311
char buf[128];
12571312
PyOS_snprintf(buf, sizeof(buf),
12581313
"free %d-sized PyTupleObject", len);

Python/pylifecycle.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,11 @@ pycore_init_global_objects(PyInterpreterState *interp)
682682

683683
_PyUnicode_InitState(interp);
684684

685+
status = _PyTuple_InitGlobalObjects(interp);
686+
if (_PyStatus_EXCEPTION(status)) {
687+
return status;
688+
}
689+
685690
return _PyStatus_OK();
686691
}
687692

Tools/scripts/deepfreeze.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
284284
return f"& {name}.ob_base"
285285

286286
def generate_tuple(self, name: str, t: Tuple[object, ...]) -> str:
287-
if len(t) == 0:
288-
return f"(PyObject *)& _Py_SINGLETON(tuple_empty)"
289287
items = [self.generate(f"{name}_{i}", it) for i, it in enumerate(t)]
290288
self.write("static")
291289
with self.indent():

Tools/scripts/generate_global_objects.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,6 @@ def generate_runtime_init(identifiers, strings):
252252
for name in sorted(identifiers):
253253
assert name.isidentifier(), name
254254
printer.write(f'INIT_ID({name}),')
255-
printer.write('')
256-
with printer.block('.tuple_empty =', ','):
257-
printer.write('.ob_base = _PyVarObject_IMMORTAL_INIT(&PyTuple_Type, 0)')
258255
printer.write(END)
259256
printer.write(after)
260257

0 commit comments

Comments
 (0)