diff --git a/.github/workflows/freethreading_tests.yml b/.github/workflows/freethreading_tests.yml new file mode 100644 index 000000000..58652e801 --- /dev/null +++ b/.github/workflows/freethreading_tests.yml @@ -0,0 +1,46 @@ +name: Free Threading Tests +on: [push, pull_request] +jobs: + freethreading_tests: + name: Free Threaded Build ${{ matrix.os.emoji }} ${{ matrix.os.name }} ${{ matrix.python }} + runs-on: ${{ matrix.os.runs-on }} + strategy: + fail-fast: false + matrix: + os: + - name: Linux + matrix: linux + emoji: 🐧 + runs-on: [ubuntu-latest] + - name: macOS intel + matrix: macos + emoji: 🍎 + runs-on: [macos-13] + - name: macOS silicon + matrix: macos + emoji: 🍎 + runs-on: [macos-14] + - name: Windows + matrix: windows + emoji: 🪟 + runs-on: [windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Install uv and set the python version + uses: astral-sh/setup-uv@v5 + with: + version: "0.7.2" + python-version: 3.13t + enable-cache: false + - name: Set up Python + run: uv python install + - name: Install Dependencies + run: | + uv pip install unittest-ft + - name: Editable build + run: | + uv pip install -v -e . + python -m pip install unittest-ft + - name: Run Tests + run: | + unittest-ft -r -f -s bitarray.test_bitarray diff --git a/bitarray/_bitarray.c b/bitarray/_bitarray.c index d917b7cc8..e892f3c3f 100644 --- a/bitarray/_bitarray.c +++ b/bitarray/_bitarray.c @@ -965,7 +965,11 @@ extend_dispatch(bitarrayobject *self, PyObject *obj) static PyObject * bitarray_all(bitarrayobject *self) { - return PyBool_FromLong(find_bit(self, 0, 0, self->nbits, 0) == -1); + Py_ssize_t errind = -1; + Py_BEGIN_CRITICAL_SECTION(self); + errind = find_bit(self, 0, 0, self->nbits, 0); + Py_END_CRITICAL_SECTION(); + return PyBool_FromLong( errind == -1); } PyDoc_STRVAR(all_doc, @@ -978,7 +982,11 @@ Note that `a.all()` is faster than `all(a)`."); static PyObject * bitarray_any(bitarrayobject *self) { - return PyBool_FromLong(find_bit(self, 1, 0, self->nbits, 0) >= 0); + Py_ssize_t errind = -1; + Py_BEGIN_CRITICAL_SECTION(self); + errind = find_bit(self, 1, 0, self->nbits, 0); + Py_END_CRITICAL_SECTION(); + return PyBool_FromLong(errind >= 0); } PyDoc_STRVAR(any_doc, @@ -1000,7 +1008,9 @@ bitarray_append(bitarrayobject *self, PyObject *value) if (resize(self, self->nbits + 1) < 0) return NULL; + Py_BEGIN_CRITICAL_SECTION(self); setbit(self, self->nbits - 1, vi); + Py_END_CRITICAL_SECTION(); Py_RETURN_NONE; } @@ -1343,7 +1353,9 @@ bitarray_invert(bitarrayobject *self, PyObject *args) PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; } + Py_BEGIN_CRITICAL_SECTION(self); self->ob_item[i / 8] ^= BITMASK(self, i); + Py_END_CRITICAL_SECTION(); Py_RETURN_NONE; } if (PySlice_Check(arg)) { @@ -1352,12 +1364,18 @@ bitarray_invert(bitarrayobject *self, PyObject *args) if (PySlice_GetIndicesEx(arg, self->nbits, &start, &stop, &step, &slicelength) < 0) return NULL; + Py_BEGIN_CRITICAL_SECTION(self); adjust_step_positive(slicelength, &start, &stop, &step); + Py_END_CRITICAL_SECTION(); + Py_BEGIN_CRITICAL_SECTION(self); invert_range(self, start, stop, step); + Py_END_CRITICAL_SECTION(); Py_RETURN_NONE; } if (arg == Py_None) { + Py_BEGIN_CRITICAL_SECTION(self); invert_span(self, 0, self->nbits); + Py_END_CRITICAL_SECTION(); Py_RETURN_NONE; } @@ -4335,5 +4353,9 @@ PyInit__bitarray(void) PyModule_AddObject(m, "__version__", PyUnicode_FromString(BITARRAY_VERSION)); +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + return m; } diff --git a/bitarray/_util.c b/bitarray/_util.c index 84abe26a2..48f64c5a1 100644 --- a/bitarray/_util.c +++ b/bitarray/_util.c @@ -2105,5 +2105,9 @@ PyInit__util(void) PyModule_AddObject(m, "_SEGSIZE", PyLong_FromSsize_t(SEGSIZE)); #endif +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + return m; }