Skip to content

Commit 6888024

Browse files
committed
gh-130599: precompute conversion constants for long()
This avoids a data race in free-threaded builds due to mutating the statically arrays at runtime. Instead, compute and initialize the constants at runtime initialization.
1 parent 9e474a9 commit 6888024

File tree

3 files changed

+38
-15
lines changed

3 files changed

+38
-15
lines changed

Include/internal/pycore_long.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ extern "C" {
4949

5050
/* runtime lifecycle */
5151

52+
extern PyStatus _PyLong_InitRuntime(void);
5253
extern PyStatus _PyLong_InitTypes(PyInterpreterState *);
5354
extern void _PyLong_FiniTypes(PyInterpreterState *interp);
5455

Objects/longobject.c

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,23 +2820,18 @@ that triggers it(!). Instead the code was tested by artificially allocating
28202820
just 1 digit at the start, so that the copying code was exercised for every
28212821
digit beyond the first.
28222822
***/
2823-
static int
2824-
long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, int base, PyLongObject **res)
2825-
{
2826-
twodigits c; /* current input character */
2827-
Py_ssize_t size_z;
2828-
int i;
2829-
int convwidth;
2830-
twodigits convmultmax, convmult;
2831-
digit *pz, *pzstop;
2832-
PyLongObject *z;
2833-
const char *p;
28342823

2835-
static double log_base_BASE[37] = {0.0e0,};
2836-
static int convwidth_base[37] = {0,};
2837-
static twodigits convmultmax_base[37] = {0,};
2824+
static double log_base_BASE[37] = {0.0e0,};
2825+
static int convwidth_base[37] = {0,};
2826+
static twodigits convmultmax_base[37] = {0,};
28382827

2839-
if (log_base_BASE[base] == 0.0) {
2828+
static void
2829+
long_precompute_base_conv(void)
2830+
{
2831+
// These constants are quick to compute (likely less than 1 μs) and
2832+
// computing them at runtime avoids hard-coding a table dependant on
2833+
// PyLong_BASE.
2834+
for (int base = 2; base <= 36; base++) {
28402835
twodigits convmax = base;
28412836
int i = 1;
28422837

@@ -2854,6 +2849,21 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
28542849
assert(i > 0);
28552850
convwidth_base[base] = i;
28562851
}
2852+
}
2853+
2854+
static int
2855+
long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, int base, PyLongObject **res)
2856+
{
2857+
twodigits c; /* current input character */
2858+
Py_ssize_t size_z;
2859+
int i;
2860+
int convwidth;
2861+
twodigits convmultmax, convmult;
2862+
digit *pz, *pzstop;
2863+
PyLongObject *z;
2864+
const char *p;
2865+
2866+
assert (log_base_BASE[base] != 0.0); // pre-computed by _PyLong_InitRuntime()
28572867

28582868
/* Create an int object that can contain the largest possible
28592869
* integer with this base and length. Note that there's no
@@ -6740,6 +6750,13 @@ PyLong_GetInfo(void)
67406750

67416751
/* runtime lifecycle */
67426752

6753+
PyStatus
6754+
_PyLong_InitRuntime(void)
6755+
{
6756+
long_precompute_base_conv();
6757+
return _PyStatus_OK();
6758+
}
6759+
67436760
PyStatus
67446761
_PyLong_InitTypes(PyInterpreterState *interp)
67456762
{

Python/pylifecycle.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@ pycore_init_runtime(_PyRuntimeState *runtime,
510510
return status;
511511
}
512512

513+
status = _PyLong_InitRuntime();
514+
if (_PyStatus_EXCEPTION(status)) {
515+
return status;
516+
}
517+
513518
status = _PyImport_Init();
514519
if (_PyStatus_EXCEPTION(status)) {
515520
return status;

0 commit comments

Comments
 (0)