From ab719e1bdb5d73cabe7c063f45f21fd295bd2f9d Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Tue, 16 Apr 2024 12:44:49 -0600 Subject: [PATCH 1/3] MNT: disable the coercion cache for the nogil build --- numpy/_core/src/multiarray/array_coercion.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/numpy/_core/src/multiarray/array_coercion.c b/numpy/_core/src/multiarray/array_coercion.c index f63dbbc77e1f..443239274ea3 100644 --- a/numpy/_core/src/multiarray/array_coercion.c +++ b/numpy/_core/src/multiarray/array_coercion.c @@ -614,10 +614,13 @@ update_shape(int curr_ndim, int *max_ndim, return success; } - +#ifndef Py_GIL_DISABLED #define COERCION_CACHE_CACHE_SIZE 5 static int _coercion_cache_num = 0; static coercion_cache_obj *_coercion_cache_cache[COERCION_CACHE_CACHE_SIZE]; +#else +#define COERCION_CACHE_CACHE_SIZE 0 +#endif /* * Steals a reference to the object. @@ -628,11 +631,14 @@ npy_new_coercion_cache( coercion_cache_obj ***next_ptr, int ndim) { coercion_cache_obj *cache; +#if COERCION_CACHE_CACHE_SIZE > 0 if (_coercion_cache_num > 0) { _coercion_cache_num--; cache = _coercion_cache_cache[_coercion_cache_num]; } - else { + else +#endif + { cache = PyMem_Malloc(sizeof(coercion_cache_obj)); } if (cache == NULL) { @@ -661,11 +667,14 @@ npy_unlink_coercion_cache(coercion_cache_obj *current) { coercion_cache_obj *next = current->next; Py_DECREF(current->arr_or_sequence); +#if COERCION_CACHE_CACHE_SIZE > 0 if (_coercion_cache_num < COERCION_CACHE_CACHE_SIZE) { _coercion_cache_cache[_coercion_cache_num] = current; _coercion_cache_num++; } - else { + else +#endif + { PyMem_Free(current); } return next; From b3b5109100e5880ce03ca93a112b25bca6578ddb Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 18 Apr 2024 09:09:08 -0600 Subject: [PATCH 2/3] TST: add a test in a new test_multithreading module --- numpy/_core/tests/test_multithreading.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 numpy/_core/tests/test_multithreading.py diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py new file mode 100644 index 000000000000..0e35408ef6b2 --- /dev/null +++ b/numpy/_core/tests/test_multithreading.py @@ -0,0 +1,15 @@ +import concurrent.futures + +import numpy as np + + +def test_parallel_errstate_creation(): + # if the coercion cache is enabled and not thread-safe, creating + # RandomState instances simultaneously leads to a data race + def func(seed): + np.random.RandomState(seed) + + with concurrent.futures.ThreadPoolExecutor(max_workers=8) as tpe: + futures = [tpe.submit(func, i) for i in range(500)] + for f in futures: + f.result() From 1d976fc448cedc1033f9f928daed5fdeac7738ea Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 18 Apr 2024 10:06:14 -0600 Subject: [PATCH 3/3] TST: skip multithreading tests on WASM --- numpy/_core/tests/test_multithreading.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index 0e35408ef6b2..8999a18a39ff 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -1,6 +1,12 @@ import concurrent.futures import numpy as np +import pytest + +from numpy.testing import IS_WASM + +if IS_WASM: + pytest.skip(allow_module_level=True, reason="no threading support in wasm") def test_parallel_errstate_creation():