🌐 AI搜索 & 代理 主页
BUG: fix double evaluation in PyArrayScalar_RETURN_BOOL_FROM_LONG (#30418)

* BUG: fix double evaluation in PyArrayScalar_RETURN_BOOL_FROM_LONG

Closes #30389

* fix: lint errors and wasm import problems
diff --git a/numpy/_core/include/numpy/arrayscalars.h b/numpy/_core/include/numpy/arrayscalars.h
index ff04806..46bc58c 100644
--- a/numpy/_core/include/numpy/arrayscalars.h
+++ b/numpy/_core/include/numpy/arrayscalars.h
@@ -173,9 +173,11 @@
 #define PyArrayScalar_True ((PyObject *)(&(_PyArrayScalar_BoolValues[1])))
 #define PyArrayScalar_FromLong(i) \
         ((PyObject *)(&(_PyArrayScalar_BoolValues[((i)!=0)])))
-#define PyArrayScalar_RETURN_BOOL_FROM_LONG(i)                  \
-        return Py_INCREF(PyArrayScalar_FromLong(i)), \
-                PyArrayScalar_FromLong(i)
+#define PyArrayScalar_RETURN_BOOL_FROM_LONG(i) do {     \
+        PyObject *obj = PyArrayScalar_FromLong(i);      \
+        Py_INCREF(obj);                                 \
+        return obj;                                     \
+} while (0)
 #define PyArrayScalar_RETURN_FALSE              \
         return Py_INCREF(PyArrayScalar_False),  \
                 PyArrayScalar_False
diff --git a/numpy/_core/tests/test_multiprocessing.py b/numpy/_core/tests/test_multiprocessing.py
new file mode 100644
index 0000000..2c5c2fc
--- /dev/null
+++ b/numpy/_core/tests/test_multiprocessing.py
@@ -0,0 +1,51 @@
+import pytest
+
+import numpy as np
+from numpy.testing import IS_WASM
+
+pytestmark = pytest.mark.thread_unsafe(
+    reason="tests in this module are explicitly multi-processed"
+)
+
+def bool_array_writer(shm_name, n):
+    # writer routine for test_read_write_bool_array
+    import time
+    from multiprocessing import shared_memory
+    shm = shared_memory.SharedMemory(name=shm_name)
+    arr = np.ndarray(n, dtype=np.bool_, buffer=shm.buf)
+    for i in range(n):
+        arr[i] = True
+        time.sleep(0.00001)
+
+def bool_array_reader(shm_name, n):
+    # reader routine for test_read_write_bool_array
+    from multiprocessing import shared_memory
+    shm = shared_memory.SharedMemory(name=shm_name)
+    arr = np.ndarray(n, dtype=np.bool_, buffer=shm.buf)
+    for i in range(n):
+        while not arr[i]:
+            pass
+
+@pytest.mark.skipif(IS_WASM,
+                    reason="WASM does not support _posixshmem")
+def test_read_write_bool_array():
+    # See: gh-30389
+    #
+    # Prior to Python 3.13, boolean scalar singletons (np.True / np.False) were
+    # regular reference-counted objects. Due to the double evaluation in
+    # PyArrayScalar_RETURN_BOOL_FROM_LONG, concurrent reads and writes of a
+    # boolean array could corrupt their refcounts, potentially causing a crash
+    # (e.g., `free(): invalid pointer`).
+    #
+    # This test creates a multi-process race between a writer and a reader to
+    # ensure that NumPy does not exhibit such failures.
+    from concurrent.futures import ProcessPoolExecutor
+    from multiprocessing import shared_memory
+    n = 10000
+    shm = shared_memory.SharedMemory(create=True, size=n)
+    with ProcessPoolExecutor(max_workers=2) as executor:
+        f_writer = executor.submit(bool_array_writer, shm.name, n)
+        f_reader = executor.submit(bool_array_reader, shm.name, n)
+    shm.unlink()
+    f_writer.result()
+    f_reader.result()