Skip to content

Commit c41eed1

Browse files
authored
bpo-40521: Make bytes singletons per interpreter (GH-21074)
Each interpreter now has its own empty bytes string and single byte character singletons. Replace STRINGLIB_EMPTY macro with STRINGLIB_GET_EMPTY() macro.
1 parent 32f2eda commit c41eed1

File tree

13 files changed

+96
-53
lines changed

13 files changed

+96
-53
lines changed

Include/internal/pycore_interp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ struct _Py_unicode_fs_codec {
6565
_Py_error_handler error_handler;
6666
};
6767

68+
struct _Py_bytes_state {
69+
PyBytesObject *characters[256];
70+
PyBytesObject *empty_string;
71+
};
72+
6873
struct _Py_unicode_state {
6974
struct _Py_unicode_fs_codec fs_codec;
7075
};
@@ -233,6 +238,7 @@ struct _is {
233238
*/
234239
PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
235240
#endif
241+
struct _Py_bytes_state bytes;
236242
struct _Py_unicode_state unicode;
237243
struct _Py_float_state float_state;
238244
/* Using a cache is very effective since typically only a single slice is

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ extern void _PyDict_Fini(PyThreadState *tstate);
6363
extern void _PyTuple_Fini(PyThreadState *tstate);
6464
extern void _PyList_Fini(PyThreadState *tstate);
6565
extern void _PySet_Fini(PyThreadState *tstate);
66-
extern void _PyBytes_Fini(void);
66+
extern void _PyBytes_Fini(PyThreadState *tstate);
6767
extern void _PyFloat_Fini(PyThreadState *tstate);
6868
extern void _PySlice_Fini(PyThreadState *tstate);
6969
extern void _PyAsyncGen_Fini(PyThreadState *tstate);
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
The tuple free lists, the empty tuple singleton, the list free list, the empty
2-
frozenset singleton, the float free list, the slice cache, the dict free lists,
3-
the frame free list, the asynchronous generator free lists, and the context
4-
free list are no longer shared by all interpreters: each interpreter now its
5-
has own free lists and caches.
1+
Each interpreter now its has own free lists, singletons and caches:
2+
3+
* Free lists: float, tuple, list, dict, frame, context,
4+
asynchronous generator.
5+
* Singletons: empty tuple, empty frozenset, empty bytes string,
6+
single byte character.
7+
* Slice cache.
8+
9+
They are no longer shared by all interpreters.

Objects/bytesobject.c

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ class bytes "PyBytesObject *" "&PyBytes_Type"
1818

1919
#include "clinic/bytesobject.c.h"
2020

21-
static PyBytesObject *characters[UCHAR_MAX + 1];
22-
static PyBytesObject *nullstring;
23-
2421
_Py_IDENTIFIER(__bytes__);
2522

2623
/* PyBytesObject_SIZE gives the basic size of a string; any memory allocation
@@ -35,6 +32,15 @@ _Py_IDENTIFIER(__bytes__);
3532
Py_LOCAL_INLINE(Py_ssize_t) _PyBytesWriter_GetSize(_PyBytesWriter *writer,
3633
char *str);
3734

35+
36+
static struct _Py_bytes_state*
37+
get_bytes_state(void)
38+
{
39+
PyInterpreterState *interp = _PyInterpreterState_GET();
40+
return &interp->bytes;
41+
}
42+
43+
3844
/*
3945
For PyBytes_FromString(), the parameter `str' points to a null-terminated
4046
string containing exactly `size' bytes.
@@ -63,9 +69,13 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc)
6369
PyBytesObject *op;
6470
assert(size >= 0);
6571

66-
if (size == 0 && (op = nullstring) != NULL) {
67-
Py_INCREF(op);
68-
return (PyObject *)op;
72+
if (size == 0) {
73+
struct _Py_bytes_state *state = get_bytes_state();
74+
op = state->empty_string;
75+
if (op != NULL) {
76+
Py_INCREF(op);
77+
return (PyObject *)op;
78+
}
6979
}
7080

7181
if ((size_t)size > (size_t)PY_SSIZE_T_MAX - PyBytesObject_SIZE) {
@@ -88,8 +98,9 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc)
8898
op->ob_sval[size] = '\0';
8999
/* empty byte string singleton */
90100
if (size == 0) {
91-
nullstring = op;
101+
struct _Py_bytes_state *state = get_bytes_state();
92102
Py_INCREF(op);
103+
state->empty_string = op;
93104
}
94105
return (PyObject *) op;
95106
}
@@ -103,11 +114,13 @@ PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
103114
"Negative size passed to PyBytes_FromStringAndSize");
104115
return NULL;
105116
}
106-
if (size == 1 && str != NULL &&
107-
(op = characters[*str & UCHAR_MAX]) != NULL)
108-
{
109-
Py_INCREF(op);
110-
return (PyObject *)op;
117+
if (size == 1 && str != NULL) {
118+
struct _Py_bytes_state *state = get_bytes_state();
119+
op = state->characters[*str & UCHAR_MAX];
120+
if (op != NULL) {
121+
Py_INCREF(op);
122+
return (PyObject *)op;
123+
}
111124
}
112125

113126
op = (PyBytesObject *)_PyBytes_FromSize(size, 0);
@@ -119,8 +132,9 @@ PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
119132
memcpy(op->ob_sval, str, size);
120133
/* share short strings */
121134
if (size == 1) {
122-
characters[*str & UCHAR_MAX] = op;
135+
struct _Py_bytes_state *state = get_bytes_state();
123136
Py_INCREF(op);
137+
state->characters[*str & UCHAR_MAX] = op;
124138
}
125139
return (PyObject *) op;
126140
}
@@ -138,13 +152,21 @@ PyBytes_FromString(const char *str)
138152
"byte string is too long");
139153
return NULL;
140154
}
141-
if (size == 0 && (op = nullstring) != NULL) {
142-
Py_INCREF(op);
143-
return (PyObject *)op;
155+
156+
struct _Py_bytes_state *state = get_bytes_state();
157+
if (size == 0) {
158+
op = state->empty_string;
159+
if (op != NULL) {
160+
Py_INCREF(op);
161+
return (PyObject *)op;
162+
}
144163
}
145-
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
146-
Py_INCREF(op);
147-
return (PyObject *)op;
164+
else if (size == 1) {
165+
op = state->characters[*str & UCHAR_MAX];
166+
if (op != NULL) {
167+
Py_INCREF(op);
168+
return (PyObject *)op;
169+
}
148170
}
149171

150172
/* Inline PyObject_NewVar */
@@ -157,11 +179,12 @@ PyBytes_FromString(const char *str)
157179
memcpy(op->ob_sval, str, size+1);
158180
/* share short strings */
159181
if (size == 0) {
160-
nullstring = op;
161182
Py_INCREF(op);
162-
} else if (size == 1) {
163-
characters[*str & UCHAR_MAX] = op;
183+
state->empty_string = op;
184+
}
185+
else if (size == 1) {
164186
Py_INCREF(op);
187+
state->characters[*str & UCHAR_MAX] = op;
165188
}
166189
return (PyObject *) op;
167190
}
@@ -1249,6 +1272,8 @@ PyBytes_AsStringAndSize(PyObject *obj,
12491272
/* -------------------------------------------------------------------- */
12501273
/* Methods */
12511274

1275+
#define STRINGLIB_GET_EMPTY() get_bytes_state()->empty_string
1276+
12521277
#include "stringlib/stringdefs.h"
12531278

12541279
#include "stringlib/fastsearch.h"
@@ -1261,6 +1286,8 @@ PyBytes_AsStringAndSize(PyObject *obj,
12611286

12621287
#include "stringlib/transmogrify.h"
12631288

1289+
#undef STRINGLIB_GET_EMPTY
1290+
12641291
PyObject *
12651292
PyBytes_Repr(PyObject *obj, int smartquotes)
12661293
{
@@ -3058,12 +3085,13 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
30583085
}
30593086

30603087
void
3061-
_PyBytes_Fini(void)
3088+
_PyBytes_Fini(PyThreadState *tstate)
30623089
{
3063-
int i;
3064-
for (i = 0; i < UCHAR_MAX + 1; i++)
3065-
Py_CLEAR(characters[i]);
3066-
Py_CLEAR(nullstring);
3090+
struct _Py_bytes_state* state = &tstate->interp->bytes;
3091+
for (int i = 0; i < UCHAR_MAX + 1; i++) {
3092+
Py_CLEAR(state->characters[i]);
3093+
}
3094+
Py_CLEAR(state->empty_string);
30673095
}
30683096

30693097
/*********************** Bytes Iterator ****************************/

Objects/stringlib/README.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ STRINGLIB_CHAR
1111

1212
the type used to hold a character (char or Py_UNICODE)
1313

14-
STRINGLIB_EMPTY
14+
STRINGLIB_GET_EMPTY()
1515

16-
a PyObject representing the empty string, only to be used if
17-
STRINGLIB_MUTABLE is 0
16+
returns a PyObject representing the empty string, only to be used if
17+
STRINGLIB_MUTABLE is 0. It must not be NULL.
1818

1919
Py_ssize_t STRINGLIB_LEN(PyObject*)
2020

Objects/stringlib/asciilib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#define STRINGLIB_CHAR Py_UCS1
1212
#define STRINGLIB_TYPE_NAME "unicode"
1313
#define STRINGLIB_PARSE_CODE "U"
14-
#define STRINGLIB_EMPTY unicode_empty
14+
#define STRINGLIB_GET_EMPTY() unicode_empty
1515
#define STRINGLIB_ISSPACE Py_UNICODE_ISSPACE
1616
#define STRINGLIB_ISLINEBREAK BLOOM_LINEBREAK
1717
#define STRINGLIB_ISDECIMAL Py_UNICODE_ISDECIMAL

Objects/stringlib/partition.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ STRINGLIB(partition)(PyObject* str_obj,
3737
#else
3838
Py_INCREF(str_obj);
3939
PyTuple_SET_ITEM(out, 0, (PyObject*) str_obj);
40-
Py_INCREF(STRINGLIB_EMPTY);
41-
PyTuple_SET_ITEM(out, 1, (PyObject*) STRINGLIB_EMPTY);
42-
Py_INCREF(STRINGLIB_EMPTY);
43-
PyTuple_SET_ITEM(out, 2, (PyObject*) STRINGLIB_EMPTY);
40+
PyObject *empty = (PyObject*)STRINGLIB_GET_EMPTY();
41+
assert(empty != NULL);
42+
Py_INCREF(empty);
43+
PyTuple_SET_ITEM(out, 1, empty);
44+
Py_INCREF(empty);
45+
PyTuple_SET_ITEM(out, 2, empty);
4446
#endif
4547
return out;
4648
}
@@ -90,10 +92,12 @@ STRINGLIB(rpartition)(PyObject* str_obj,
9092
return NULL;
9193
}
9294
#else
93-
Py_INCREF(STRINGLIB_EMPTY);
94-
PyTuple_SET_ITEM(out, 0, (PyObject*) STRINGLIB_EMPTY);
95-
Py_INCREF(STRINGLIB_EMPTY);
96-
PyTuple_SET_ITEM(out, 1, (PyObject*) STRINGLIB_EMPTY);
95+
PyObject *empty = (PyObject*)STRINGLIB_GET_EMPTY();
96+
assert(empty != NULL);
97+
Py_INCREF(empty);
98+
PyTuple_SET_ITEM(out, 0, empty);
99+
Py_INCREF(empty);
100+
PyTuple_SET_ITEM(out, 1, empty);
97101
Py_INCREF(str_obj);
98102
PyTuple_SET_ITEM(out, 2, (PyObject*) str_obj);
99103
#endif

Objects/stringlib/stringdefs.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#ifndef STRINGLIB_STRINGDEFS_H
22
#define STRINGLIB_STRINGDEFS_H
33

4+
#ifndef STRINGLIB_GET_EMPTY
5+
# error "STRINGLIB_GET_EMPTY macro must be defined"
6+
#endif
7+
48
/* this is sort of a hack. there's at least one place (formatting
59
floats) where some stringlib code takes a different path if it's
610
compiled as unicode. */
@@ -13,7 +17,6 @@
1317
#define STRINGLIB_CHAR char
1418
#define STRINGLIB_TYPE_NAME "string"
1519
#define STRINGLIB_PARSE_CODE "S"
16-
#define STRINGLIB_EMPTY nullstring
1720
#define STRINGLIB_ISSPACE Py_ISSPACE
1821
#define STRINGLIB_ISLINEBREAK(x) ((x == '\n') || (x == '\r'))
1922
#define STRINGLIB_ISDECIMAL(x) ((x >= '0') && (x <= '9'))

Objects/stringlib/ucs1lib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#define STRINGLIB_CHAR Py_UCS1
1212
#define STRINGLIB_TYPE_NAME "unicode"
1313
#define STRINGLIB_PARSE_CODE "U"
14-
#define STRINGLIB_EMPTY unicode_empty
14+
#define STRINGLIB_GET_EMPTY() unicode_empty
1515
#define STRINGLIB_ISSPACE Py_UNICODE_ISSPACE
1616
#define STRINGLIB_ISLINEBREAK BLOOM_LINEBREAK
1717
#define STRINGLIB_ISDECIMAL Py_UNICODE_ISDECIMAL

Objects/stringlib/ucs2lib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#define STRINGLIB_CHAR Py_UCS2
1212
#define STRINGLIB_TYPE_NAME "unicode"
1313
#define STRINGLIB_PARSE_CODE "U"
14-
#define STRINGLIB_EMPTY unicode_empty
14+
#define STRINGLIB_GET_EMPTY() unicode_empty
1515
#define STRINGLIB_ISSPACE Py_UNICODE_ISSPACE
1616
#define STRINGLIB_ISLINEBREAK BLOOM_LINEBREAK
1717
#define STRINGLIB_ISDECIMAL Py_UNICODE_ISDECIMAL

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy