Skip to content

Commit 4ba96ef

Browse files
authored
fix: thread safety selecting codec (#1041)
libbase64 was re initialized on each call. It was likely harmless, at least on most common platform. It's also a bit more performant although not significant.
1 parent 49e273f commit 4ba96ef

1 file changed

Lines changed: 35 additions & 26 deletions

File tree

src/pybase64/_pybase64.c

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ typedef struct pybase64_state {
4242
PyObject *ignoreCharsNoPadding;
4343
uint32_t active_simd_flag;
4444
uint32_t simd_flags;
45-
int libbase64_simd_flag;
4645
} pybase64_state;
4746

4847
#if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x030d0000
@@ -606,10 +605,6 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
606605
#endif
607606
char* dst_start;
608607
char* dst;
609-
pybase64_state *state = (pybase64_state*)PyModule_GetState(self);
610-
if (state == NULL) { /* GCOVR_EXCL_BR_WITHOUT_HIT: 1/2 */
611-
return NULL; /* GCOVR_EXCL_LINE */
612-
}
613608

614609
if (wrapcol < 0) {
615610
PyErr_SetString(PyExc_ValueError, "wrapcol must be >= 0");
@@ -679,8 +674,6 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
679674
/* not interacting with Python objects from here, release the GIL */
680675
Py_BEGIN_ALLOW_THREADS
681676

682-
int const libbase64_simd_flag = state->libbase64_simd_flag;
683-
684677
if (flags & PYBASE64_FLAGS_APPEND_NEW_LINE) {
685678
out_len--; /* only consider len without new line terminator */
686679
}
@@ -696,7 +689,7 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
696689
while (out_len > dst_slice) {
697690
size_t dst_len = (size_t)wrapcol;
698691

699-
base64_encode(src, src_slice, dst, &dst_len, libbase64_simd_flag);
692+
base64_encode(src, src_slice, dst, &dst_len, 0);
700693
translate_inplace(dst, dst_len, alphabet);
701694
dst[dst_len] = '\n';
702695

@@ -706,14 +699,14 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
706699
dst += dst_slice;
707700
}
708701
remainder = out_len;
709-
base64_encode(src, len, dst, &remainder, libbase64_simd_flag);
702+
base64_encode(src, len, dst, &remainder, 0);
710703
translate_inplace(dst, remainder, alphabet);
711704
dst += remainder;
712705
}
713706
else {
714707
while (out_len > dst_slice) {
715708
size_t dst_len = (size_t)wrapcol;
716-
base64_encode(src, src_slice, dst, &dst_len, libbase64_simd_flag);
709+
base64_encode(src, src_slice, dst, &dst_len, 0);
717710
dst[dst_len] = '\n';
718711

719712
len -= src_slice;
@@ -722,7 +715,7 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
722715
dst += dst_slice;
723716
}
724717
remainder = out_len;
725-
base64_encode(src, len, dst, &remainder, libbase64_simd_flag);
718+
base64_encode(src, len, dst, &remainder, 0);
726719
dst += remainder;
727720
}
728721
}
@@ -737,7 +730,7 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
737730
while (out_len > dst_slice) {
738731
size_t dst_len = dst_slice;
739732

740-
base64_encode(src, src_slice, dst, &dst_len, libbase64_simd_flag);
733+
base64_encode(src, src_slice, dst, &dst_len, 0);
741734
translate_inplace(dst, dst_slice, alphabet);
742735

743736
len -= src_slice;
@@ -746,12 +739,12 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
746739
dst += dst_slice;
747740
}
748741
remainder = out_len;
749-
base64_encode(src, len, dst, &out_len, libbase64_simd_flag);
742+
base64_encode(src, len, dst, &out_len, 0);
750743
translate_inplace(dst, remainder, alphabet);
751744
dst += remainder;
752745
}
753746
else {
754-
base64_encode(buffer->buf, buffer->len, dst, &out_len, libbase64_simd_flag);
747+
base64_encode(buffer->buf, buffer->len, dst, &out_len, 0);
755748
dst += out_len;
756749
}
757750
if (flags & PYBASE64_FLAGS_NO_PADDING) {
@@ -1160,10 +1153,9 @@ static PyObject* pybase64_decode_impl(PyObject* self, PyObject* args, PyObject *
11601153
/* not interacting with Python objects from here, release the GIL */
11611154
Py_BEGIN_ALLOW_THREADS
11621155

1163-
int const b64_flags = state->libbase64_simd_flag;
11641156
struct base64_state b64_state;
11651157

1166-
base64_stream_decode_init(&b64_state, b64_flags);
1158+
base64_stream_decode_init(&b64_state, 0);
11671159

11681160
while (len > src_slice) {
11691161
size_t dst_len = dst_slice;
@@ -1222,7 +1214,7 @@ static PyObject* pybase64_decode_impl(PyObject* self, PyObject* args, PyObject *
12221214
/* not interacting with Python objects from here, release the GIL */
12231215
Py_BEGIN_ALLOW_THREADS
12241216

1225-
result = base64_decode(source, source_len, dest, &out_len, state->libbase64_simd_flag);
1217+
result = base64_decode(source, source_len, dest, &out_len, 0);
12261218

12271219
/* restore the GIL */
12281220
Py_END_ALLOW_THREADS
@@ -1367,63 +1359,80 @@ static PyObject* pybase64_get_simd_flags_compile(PyObject* self, PyObject* arg)
13671359

13681360
static void set_simd_path(pybase64_state* state, uint32_t flag)
13691361
{
1362+
#if PY_VERSION_HEX >= 0x030d0000
1363+
static PyMutex mutex = {0};
1364+
#endif
1365+
static int b64_cpu_flags_mem = 0;
1366+
int b64_cpu_flags;
1367+
13701368
flag &= state->simd_flags; /* clean-up with allowed flags */
13711369

1370+
#if PY_VERSION_HEX >= 0x030d0000
1371+
PyMutex_Lock(&mutex);
1372+
#endif
13721373
if (0) {
13731374
}
13741375
#if BASE64_WITH_NEON64
13751376
else if (flag & PYBASE64_NEON) {
13761377
state->active_simd_flag = PYBASE64_NEON;
1377-
state->libbase64_simd_flag = BASE64_FORCE_NEON64;
1378+
b64_cpu_flags = BASE64_FORCE_NEON64;
13781379
}
13791380
#endif
13801381
#if BASE64_WITH_NEON32
13811382
else if (flag & PYBASE64_NEON) {
13821383
state->active_simd_flag = PYBASE64_NEON;
1383-
state->libbase64_simd_flag = BASE64_FORCE_NEON32;
1384+
b64_cpu_flags = BASE64_FORCE_NEON32;
13841385
}
13851386
#endif
13861387

13871388
#if BASE64_WITH_AVX512
13881389
else if (flag & PYBASE64_AVX512VBMI) {
13891390
state->active_simd_flag = PYBASE64_AVX512VBMI;
1390-
state->libbase64_simd_flag = BASE64_FORCE_AVX512;
1391+
b64_cpu_flags = BASE64_FORCE_AVX512;
13911392
}
13921393
#endif
13931394
#if BASE64_WITH_AVX2
13941395
else if (flag & PYBASE64_AVX2) {
13951396
state->active_simd_flag = PYBASE64_AVX2;
1396-
state->libbase64_simd_flag = BASE64_FORCE_AVX2;
1397+
b64_cpu_flags = BASE64_FORCE_AVX2;
13971398
}
13981399
#endif
13991400
#if BASE64_WITH_AVX
14001401
else if (flag & PYBASE64_AVX) {
14011402
state->active_simd_flag = PYBASE64_AVX;
1402-
state->libbase64_simd_flag = BASE64_FORCE_AVX;
1403+
b64_cpu_flags = BASE64_FORCE_AVX;
14031404
}
14041405
#endif
14051406
#if BASE64_WITH_SSE42
14061407
else if (flag & PYBASE64_SSE42) {
14071408
state->active_simd_flag = PYBASE64_SSE42;
1408-
state->libbase64_simd_flag = BASE64_FORCE_SSE42;
1409+
b64_cpu_flags = BASE64_FORCE_SSE42;
14091410
}
14101411
#endif
14111412
#if BASE64_WITH_SSE41
14121413
else if (flag & PYBASE64_SSE41) {
14131414
state->active_simd_flag = PYBASE64_SSE41;
1414-
state->libbase64_simd_flag = BASE64_FORCE_SSE41;
1415+
b64_cpu_flags = BASE64_FORCE_SSE41;
14151416
}
14161417
#endif
14171418
#if BASE64_WITH_SSSE3
14181419
else if (flag & PYBASE64_SSSE3) {
14191420
state->active_simd_flag = PYBASE64_SSSE3;
1420-
state->libbase64_simd_flag = BASE64_FORCE_SSSE3;
1421+
b64_cpu_flags = BASE64_FORCE_SSSE3;
14211422
}
14221423
#endif
14231424
else {
14241425
state->active_simd_flag = PYBASE64_NONE;
1425-
state->libbase64_simd_flag = BASE64_FORCE_PLAIN;
1426+
b64_cpu_flags = BASE64_FORCE_PLAIN;
14261427
}
1428+
if (b64_cpu_flags_mem != b64_cpu_flags) {
1429+
struct base64_state b64_state;
1430+
base64_stream_encode_init(&b64_state, b64_cpu_flags);
1431+
b64_cpu_flags_mem = b64_cpu_flags;
1432+
}
1433+
#if PY_VERSION_HEX >= 0x030d0000
1434+
PyMutex_Unlock(&mutex);
1435+
#endif
14271436
}
14281437

14291438
static PyObject* pybase64_set_simd_path(PyObject* self, PyObject* arg)

0 commit comments

Comments
 (0)