-
Notifications
You must be signed in to change notification settings - Fork 16
ENH: Free threading support #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a02d49b
to
18a050e
Compare
Leaving the thread sanitizer race warnings here for posterity.. Races==================
WARNING: ThreadSanitizer: data race (pid=933)
Read of size 8 at 0x7f0890502298 by main thread:
#0 PyUnstable_InterpreterFrame_GetLine /tmp/python-build.20250221213836.92/Python-3.13.2/Python/frame.c:152:16 (libpython3.13t.so.1.0+0x3769a8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#1 PyFrame_GetLineNumber /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/frameobject.c:901:12 (libpython3.13t.so.1.0+0x16368d) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#2 frame_getlineno /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/frameobject.c:907:18 (libpython3.13t.so.1.0+0x16368d)
#3 getset_get /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/descrobject.c:193:16 (libpython3.13t.so.1.0+0x136dc8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#4 _PyObject_GenericGetAttrWithDict /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:1665:19 (libpython3.13t.so.1.0+0x1cd525) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#5 PyObject_GenericGetAttr /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:1747:12 (libpython3.13t.so.1.0+0x1cd3[72](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:73)) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#6 PyObject_GetAttr /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:1261:18 (libpython3.13t.so.1.0+0x1cbc97) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#7 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:3766:28 (libpython3.13t.so.1.0+0x3225d3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#8 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x152aa8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#9 gen_send_ex2 /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:229:24 (libpython3.13t.so.1.0+0x152aa8)
#10 gen_iternext /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:589:9 (libpython3.13t.so.1.0+0x150299) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#11 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:2786:24 (libpython3.13t.so.1.0+0x31f0ef) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#12 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x152aa8) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#13 gen_send_ex2 /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:229:24 (libpython3.13t.so.1.0+0x152aa8)
#14 gen_iternext /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/genobject.c:589:9 (libpython3.13t.so.1.0+0x150299) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#15 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:2786:24 (libpython3.13t.so.1.0+0x31f0ef) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#16 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#17 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
#18 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#19 _PyVectorcall_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:2[73](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:74):16 (libpython3.13t.so.1.0+0x1227c3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#20 _PyObject_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:348:16 (libpython3.13t.so.1.0+0x1227c3)
#21 _PyErr_CheckSignalsTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Modules/signalmodule.c:1855:22 (libpython3.13t.so.1.0+0x42dbe3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#22 handle_signals /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval_gil.c:828:9 (libpython3.13t.so.1.0+0x385f87) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#23 _PyEval_MakePendingCalls /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval_gil.c:1045:15 (libpython3.13t.so.1.0+0x385f87)
#24 Py_MakePendingCalls /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval_gil.c:1073:12 (libpython3.13t.so.1.0+0x385f87)
#25 ThreadHandle_join /tmp/python-build.20250221213836.92/Python-3.13.2/./Modules/_threadmodule.c:515:17 (libpython3.13t.so.1.0+0x499198) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#26 PyThreadHandleObject_join /tmp/python-build.20250221213836.92/Python-3.13.2/./Modules/_threadmodule.c:643:9 (libpython3.13t.so.1.0+0x4999b3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#37 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:166:16 (libpython3.13t.so.1.0+0x12258a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#38 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x12258a)
#39 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:1502:19 (libpython3.13t.so.1.0+0x31a98b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#40 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#41 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
#42 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#43 _PyObject_VectorcallDictTstate /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:146:15 (libpython3.13t.so.1.0+0x121680) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#44 _PyObject_Call_Prepend /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:504:24 (libpython3.13t.so.1.0+0x1231a6) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#45 slot_tp_call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/typeobject.c:9539:15 (libpython3.13t.so.1.0+0x22f76a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#46 _PyObject_MakeTpCall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:242:18 (libpython3.13t.so.1.0+0x12192e) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#47 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:166:16 (libpython3.13t.so.1.0+0x12258a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#48 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x12258a)
#49 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:1502:19 (libpython3.13t.so.1.0+0x31a98b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#50 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#51 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
#52 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#53 _PyObject_VectorcallDictTstate /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:146:15 (libpython3.13t.so.1.0+0x121680) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#54 _PyObject_Call_Prepend /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:504:24 (libpython3.13t.so.1.0+0x1231a6) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#55 slot_tp_call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/typeobject.c:9539:15 (libpython3.13t.so.1.0+0x22f76a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#56 _PyObject_MakeTpCall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:242:18 (libpython3.13t.so.1.0+0x12192e) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#57 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:166:16 (libpython3.13t.so.1.0+0x12258a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#58 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x12258a)
#59 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:1502:19 (libpython3.13t.so.1.0+0x31a98b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#60 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314284) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#61 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314284)
#62 PyEval_EvalCode /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:602:21 (libpython3.13t.so.1.0+0x314284)
#63 builtin_exec_impl /tmp/python-build.20250221213836.92/Python-3.13.2/Python/bltinmodule.c:1145:17 (libpython3.13t.so.1.0+0x30eb96) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#64 builtin_exec /tmp/python-build.20250221213836.92/Python-3.13.2/Python/clinic/bltinmodule.c.h:556:20 (libpython3.13t.so.1.0+0x30eb96)
#65 cfunction_vectorcall_FASTCALL_KEYWORDS /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/methodobject.c:441:24 (libpython3.13t.so.1.0+0x1c1853) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#66 _PyObject_VectorcallTstate /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_call.h:168:11 (libpython3.13t.so.1.0+0x1224cc) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#67 PyObject_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:327:12 (libpython3.13t.so.1.0+0x1224cc)
#68 _PyEval_EvalFrameDefault /tmp/python-build.20250221213836.92/Python-3.13.2/Python/generated_cases.c.h:813:23 (libpython3.13t.so.1.0+0x317d5b) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#69 _PyEval_EvalFrame /tmp/python-build.20250221213836.92/Python-3.13.2/./Include/internal/pycore_ceval.h:119:16 (libpython3.13t.so.1.0+0x314599) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#70 _PyEval_Vector /tmp/python-build.20250221213836.92/Python-3.13.2/Python/ceval.c:1812:12 (libpython3.13t.so.1.0+0x314599)
#71 _PyFunction_Vectorcall /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c (libpython3.13t.so.1.0+0x122b51) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#72 _PyVectorcall_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:273:16 (libpython3.13t.so.1.0+0x1227c3) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#73 _PyObject_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:348:16 (libpython3.13t.so.1.0+0x1227c3)
#[74](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:75) PyObject_Call /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/call.c:373:12 (libpython3.13t.so.1.0+0x122847) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#75 pymain_run_module /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:349:14 (libpython3.13t.so.1.0+0x40be9a) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#76 pymain_run_python /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:691:21 (libpython3.13t.so.1.0+0x40b5bb) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#77 Py_RunMain /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:7[76](https://github.com/Quansight-Labs/ndindex/actions/runs/14653425582/job/41124094812?pr=199#step:8:77):5 (libpython3.13t.so.1.0+0x40b5bb)
#78 pymain_main /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:806:12 (libpython3.13t.so.1.0+0x40bc68) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#79 Py_BytesMain /tmp/python-build.20250221213836.92/Python-3.13.2/Modules/main.c:830:12 (libpython3.13t.so.1.0+0x40bcec) (BuildId: 9359595b9a1895ecc20bf063638092944065b5c6)
#80 main /tmp/python-build.20250221213836.92/Python-3.13.2/./Programs/python.c:15:12 (python3.13+0xe8c9b) (BuildId: 3cc7488ffc292c8dd5400212ea3a107a04537e72)
SUMMARY: ThreadSanitizer: data race /tmp/python-build.20250221213836.92/Python-3.13.2/Objects/object.c:2465:23 in new_reference
================== |
Rather baffling doc building error, also in CircleCI: Doc build errors/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:176: ERROR: Error in "plot" directive:
unknown option: "output-base-name".
.. plot::
:context: reset
:include-source: True
:output-base-name: plot-{counter}
:alt: A plot of 4*x*np.sin(x) - x**2/4 - 2*x from -10 to 10. The curve crosses the x-axis several times at irregular intervals.
:caption: Plot of :math:`y = 4x\sin(x) - \frac{x^2}{4} - 2x`
>>> import matplotlib.pyplot as plt
>>> x = np.linspace(-10, 10, 10000) # 10000 evenly spaced points between -10 and 10
>>> y = 4*x*np.sin(x) - x**2/4 - 2*x # our function
>>> plt.scatter(x, y, marker=',', s=1)
<matplotlib.collections.PathCollection object at ...> [docutils]
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:194: ERROR: Error in "plot" directive:
unknown option: "output-base-name".
.. plot::
:context: close-figs
:include-source: True
:output-base-name: plot-{counter}
:alt: A plot of only the parts of 4*x*np.sin(x) - x**2/4 - 2*x that are above the x-axis.
:caption: Plot of :math:`y = 4x\sin(x) - \frac{x^2}{4} - 2x` where :math:`y > 0`
>>> plt.scatter(x[y > 0], y[y > 0], marker=',', s=1)
<matplotlib.collections.PathCollection object at ...> [docutils]
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:387: ERROR: Error in "plot" directive:
unknown option: "output-base-name".
.. plot::
:context: reset
:include-source: True
:output-base-name: astronaut-{counter}
:alt: An image of an astronaut, which is represented as a shape (512, 512, 3) array.
>>> def imshow(image, title):
... import matplotlib.pyplot as plt
... plt.axis('off')
... plt.title(title)
... plt.imshow(image)
>>> from skimage.data import astronaut
>>> image = astronaut()
>>> image.shape
(512, 512, 3)
>>> imshow(image, "Original Image") [docutils]
/home/runner/work/ndindex/ndindex/docs/indexing-guide/multidimensional-indices/boolean-arrays.md:[41](https://github.com/Quansight-Labs/ndindex/actions/runs/14654158449/job/41126132114?pr=199#step:6:42)0: ERROR: Error in "plot" directive:
unknown option: "output-base-name". |
Test failures are actually coverage issues so far (TSAN aside). |
The docs build error is because the docs require matplotlib/matplotlib#28187. I thought I had the CI set up to use that PR when building. |
NDIndex objects are all designed to be 100% immutable. Even the objects that wrap arrays (which aren't yet Cythonized) copy them and set them to be read-only so that they can be immutable and hashable. |
Oh, regarding the docs thing, I guess I changed the option name in the matplotlib PR and never updated it here. |
I'm not sure whether the TSAN tests are hanging or if they're just really slow. I would avoid TSAN if it adds a ton of overhead to the tests. ASAN will likely be a lot faster, although it won't catch as many things. It will catch serious issues, though. |
The docs build should be fixed on main. |
27f43fa
to
9d74e74
Compare
ndindex/_ft_globals.pyx
Outdated
|
||
# Slow path: Use critical section for initialization | ||
# Use the dedicated module-level lock object | ||
with cython.critical_section(_numpy_init_lock_obj): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to use e.g. std::call_once
from the C++ standard library. There are wrappers for the C and C++ standard library threading primitives shipped by Cython. C++ standard library threads have wider support so they're usually the best bet.
Critical sections are suspended when you call into the CPython C API (e.g. with the getattr
or 'numpy' in sys.modules
below), so this critical section isn't actually protecting the race you're trying to protect against from occurring. You could also use a PyMutex
in the same way you're using a critical section, also no need to allocate a dummy object. PyMutex
has the nice feature that it's impossible to deadlock with PyMutex
on the GIL-enabled build.
With both options you might need to worry about the Python code executed by the getattr
or sys.modules
accessed somehow reentering this code. I don't think that's possible, so deadlocks shouldn't be possible either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another example where I did something similar is the runtime import cache in NumPy: https://github.com/numpy/numpy/blob/aa7c4f5d59ee2b478eae9c7fa8759d59a8365c2d/numpy/_core/src/common/npy_import.h#L86-L109. Note the need for atomic loads to ensure that the single-initialization is thread-safe.
A sanity check for the CI approach here: I did not expect a TSan job to be added, or ASan. We haven't done that in any project except for NumPy (and maybe PyO3?). It's probably only going to catch things in other projects (CPython, NumPy, Cython) and be worth way more trouble than it's worth - all I'd expect is future maintenance headaches. The way I'd deal with this is to run it once in a Docker container, which is quick and easy with https://github.com/nascheme/cpython_sanity, and if it turns up nothing then check the box that it was clean in a comment or the PR description here. The standard approach to testing is to add a single |
11670aa
to
fdf069b
Compare
It's happening on |
We should replace 3.13-dev with just 3.13 in the CI (we could also add 3.14-dev, but that can be done in a separate PR). |
I'll take this along too. |
It's too early for 3.14-dev by the way: that attempts to build |
474b0f5
to
3002c73
Compare
For PyPy 39 xref: Quansight-Labs#199 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better now, just a few minor comments and suggestions inline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good now, it's pretty clean. Thanks Rohit, Nathan, and Aaron!
Oh actually, it needs some history rewriting since there's no squash-merge enabled. Let me just do that right now and get this over the finish line. |
Using a thin wrapper around `std::call_once` Co-authored-by: ngoldbaum <[email protected]>
For PyPy 39 xref: Quansight-Labs#199 (comment) Needed after switching Cython to C++ mode in `_tuple.pyx` Co-authored-by: Ralf Gommers <[email protected]>
Closes #198. Basically:
a critical sectionmodule scope forglobal
cache variables in CythonWith the slow-and-fast path structuring recommended in the ft-guideft
buildsSplits theUses conditional compilation viaglobal
sections out into their own files since earliercython
versions cannot handlecritical_section
IF
now.Draft until I get the CI right for
The testing bit is mostly taken from this recent NumPy PR, but since there aren't any tests which use
threading
(nor AFAIK should there be, the NDIndex objects seem pretty immutable), might not even need theTSAN
run.