From e794d197f5d143c235205937fbba6500ac0725e0 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:05:30 -0500 Subject: [PATCH 1/8] fix: use freethreading-supported `_PySet_NextItemRef` where possible --- numba_cuda/numba/cuda/cext/_typeof.cpp | 23 ++++++++++++++++++----- numba_cuda/numba/cuda/core/pythonapi.py | 5 ++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/numba_cuda/numba/cuda/cext/_typeof.cpp b/numba_cuda/numba/cuda/cext/_typeof.cpp index 8c665cd8e..f3db54cfc 100644 --- a/numba_cuda/numba/cuda/cext/_typeof.cpp +++ b/numba_cuda/numba/cuda/cext/_typeof.cpp @@ -17,11 +17,24 @@ #include #endif -#if (PY_MAJOR_VERSION >= 3) && ((PY_MINOR_VERSION == 13) || (PY_MINOR_VERSION == 14)) - #ifndef Py_BUILD_CORE - #define Py_BUILD_CORE 1 +#ifndef Py_BUILD_CORE +#define Py_BUILD_CORE 1 +#endif + +#include "internal/pycore_setobject.h" + +#ifdef Py_BUILD_CORE +#undef Py_BUILD_CORE +#endif + +#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION >= 13) + #ifndef PySet_NextEntry + #define PySet_NextEntry _PySet_NextEntryRef + #endif +#else + #ifndef PySet_NextEntry + #define PySet_NextEntry _PySet_NextEntry #endif - #include "internal/pycore_setobject.h" // _PySet_NextEntry() #endif @@ -411,7 +424,7 @@ compute_fingerprint(string_writer_t *w, PyObject *val) PyObject *item; Py_ssize_t pos = 0; /* Only one item is considered, as in typeof.py */ - if (!_PySet_NextEntry(val, &pos, &item, &h)) { + if (!PySet_NextEntry(val, &pos, &item, &h)) { /* Empty set */ PyErr_SetString(PyExc_ValueError, "cannot compute fingerprint of empty set"); diff --git a/numba_cuda/numba/cuda/core/pythonapi.py b/numba_cuda/numba/cuda/core/pythonapi.py index 5a3fda59f..e5da0ad00 100644 --- a/numba_cuda/numba/cuda/core/pythonapi.py +++ b/numba_cuda/numba/cuda/core/pythonapi.py @@ -875,7 +875,10 @@ def set_next_entry(self, set, posptr, keyptr, hashptr): self.py_hash_t.as_pointer(), ], ) - fn = self._get_function(fnty, name="_PySet_NextEntry") + name = "_PySet_NextEntry" + if sys.version_info[:2] >= (3, 13): + name += "Ref" + fn = self._get_function(fnty, name=name) return self.builder.call(fn, (set, posptr, keyptr, hashptr)) @contextlib.contextmanager From 5e41e4ad1a7a696bc017ff888691ccfeb9cc6f3b Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:15:57 -0500 Subject: [PATCH 2/8] chore: decref correctly --- numba_cuda/numba/cuda/cext/_typeof.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/numba_cuda/numba/cuda/cext/_typeof.cpp b/numba_cuda/numba/cuda/cext/_typeof.cpp index f3db54cfc..94d206c96 100644 --- a/numba_cuda/numba/cuda/cext/_typeof.cpp +++ b/numba_cuda/numba/cuda/cext/_typeof.cpp @@ -431,7 +431,15 @@ compute_fingerprint(string_writer_t *w, PyObject *val) return -1; } TRY(string_writer_put_char, w, OP_SET); - TRY(compute_fingerprint, w, item); + if (compute_fingerprint(w, item)) { +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 + Py_XDECREF(item); +#endif + return -1; + } +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 + Py_XDECREF(item); +#endif return 0; } if (PyObject_CheckBuffer(val)) { From 332be40482bbc1604a09741c075f13d772571592 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:28:08 -0500 Subject: [PATCH 3/8] chore: revert pythonapi changes --- numba_cuda/numba/cuda/core/pythonapi.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/numba_cuda/numba/cuda/core/pythonapi.py b/numba_cuda/numba/cuda/core/pythonapi.py index e5da0ad00..5a3fda59f 100644 --- a/numba_cuda/numba/cuda/core/pythonapi.py +++ b/numba_cuda/numba/cuda/core/pythonapi.py @@ -875,10 +875,7 @@ def set_next_entry(self, set, posptr, keyptr, hashptr): self.py_hash_t.as_pointer(), ], ) - name = "_PySet_NextEntry" - if sys.version_info[:2] >= (3, 13): - name += "Ref" - fn = self._get_function(fnty, name=name) + fn = self._get_function(fnty, name="_PySet_NextEntry") return self.builder.call(fn, (set, posptr, keyptr, hashptr)) @contextlib.contextmanager From 50649164fca17faec1d759bd76f76c432a4b448a Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:28:21 -0500 Subject: [PATCH 4/8] chore: handle cleanup in _typeof --- numba_cuda/numba/cuda/cext/_typeof.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/numba_cuda/numba/cuda/cext/_typeof.cpp b/numba_cuda/numba/cuda/cext/_typeof.cpp index 94d206c96..a415a861a 100644 --- a/numba_cuda/numba/cuda/cext/_typeof.cpp +++ b/numba_cuda/numba/cuda/cext/_typeof.cpp @@ -431,16 +431,15 @@ compute_fingerprint(string_writer_t *w, PyObject *val) return -1; } TRY(string_writer_put_char, w, OP_SET); + int ret = 0; if (compute_fingerprint(w, item)) { -#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 - Py_XDECREF(item); -#endif - return -1; + ret = -1; } #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 + // extra ref if using python >= 3.13 Py_XDECREF(item); #endif - return 0; + return ret; } if (PyObject_CheckBuffer(val)) { Py_buffer buf; From cc774bfc0b9e7e9114a499d6190796e4595d1305 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:30:45 -0500 Subject: [PATCH 5/8] chore: document the pythonapi hiccup --- numba_cuda/numba/cuda/core/pythonapi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/numba_cuda/numba/cuda/core/pythonapi.py b/numba_cuda/numba/cuda/core/pythonapi.py index 5a3fda59f..1e4fa5ed1 100644 --- a/numba_cuda/numba/cuda/core/pythonapi.py +++ b/numba_cuda/numba/cuda/core/pythonapi.py @@ -875,6 +875,9 @@ def set_next_entry(self, set, posptr, keyptr, hashptr): self.py_hash_t.as_pointer(), ], ) + # `_PySet_NextEntry` returns a borrowed reference to the key, which is + # generally not expected for iterators--which is the place where this + # is used internally. Perhaps we should revisit this at some point fn = self._get_function(fnty, name="_PySet_NextEntry") return self.builder.call(fn, (set, posptr, keyptr, hashptr)) From d9d1460393778a4315317ef2b6327c3a7c8e0447 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:37:26 -0500 Subject: [PATCH 6/8] chore: adjust include to be only present in 3.13+ --- numba_cuda/numba/cuda/cext/_typeof.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/numba_cuda/numba/cuda/cext/_typeof.cpp b/numba_cuda/numba/cuda/cext/_typeof.cpp index a415a861a..27fe55fea 100644 --- a/numba_cuda/numba/cuda/cext/_typeof.cpp +++ b/numba_cuda/numba/cuda/cext/_typeof.cpp @@ -21,22 +21,22 @@ #define Py_BUILD_CORE 1 #endif +#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION >= 13) +// required include from Python 3.13+ #include "internal/pycore_setobject.h" +#ifndef PySet_NextEntry +#define PySet_NextEntry _PySet_NextEntryRef +#endif +#else +#ifndef PySet_NextEntry +#define PySet_NextEntry _PySet_NextEntry +#endif +#endif #ifdef Py_BUILD_CORE #undef Py_BUILD_CORE #endif -#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION >= 13) - #ifndef PySet_NextEntry - #define PySet_NextEntry _PySet_NextEntryRef - #endif -#else - #ifndef PySet_NextEntry - #define PySet_NextEntry _PySet_NextEntry - #endif -#endif - /* Cached typecodes for basic scalar types */ static int tc_int8; From 3aa3d5cbd06c2712386af4a3cfd75732a35fc1be Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:44:16 -0500 Subject: [PATCH 7/8] refactor: make error-on-exit a bit cleaner --- numba_cuda/numba/cuda/cext/_typeof.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/numba_cuda/numba/cuda/cext/_typeof.cpp b/numba_cuda/numba/cuda/cext/_typeof.cpp index 27fe55fea..9df61fe61 100644 --- a/numba_cuda/numba/cuda/cext/_typeof.cpp +++ b/numba_cuda/numba/cuda/cext/_typeof.cpp @@ -430,17 +430,32 @@ compute_fingerprint(string_writer_t *w, PyObject *val) "cannot compute fingerprint of empty set"); return -1; } - TRY(string_writer_put_char, w, OP_SET); - int ret = 0; + + if (string_writer_put_char(w, OP_SET)) { + goto fingerprint_error; + } + if (compute_fingerprint(w, item)) { - ret = -1; + goto fingerprint_error; } + + goto fingerprint_success; + +fingerprint_error: #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 // extra ref if using python >= 3.13 Py_XDECREF(item); #endif - return ret; + return -1; + +fingerprint_success: +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 + // extra ref if using python >= 3.13 + Py_XDECREF(item); +#endif + return 0; } + if (PyObject_CheckBuffer(val)) { Py_buffer buf; int flags = PyBUF_ND | PyBUF_STRIDES | PyBUF_FORMAT; From d53b1f14a91f49e38b46703305355f452c88f202 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Tue, 6 Jan 2026 07:17:36 -0500 Subject: [PATCH 8/8] fix: ensure that the nextentry ref API call locks the set --- numba_cuda/numba/cuda/cext/_typeof.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/numba_cuda/numba/cuda/cext/_typeof.cpp b/numba_cuda/numba/cuda/cext/_typeof.cpp index 9df61fe61..7d5905fb7 100644 --- a/numba_cuda/numba/cuda/cext/_typeof.cpp +++ b/numba_cuda/numba/cuda/cext/_typeof.cpp @@ -423,8 +423,21 @@ compute_fingerprint(string_writer_t *w, PyObject *val) Py_hash_t h; PyObject *item; Py_ssize_t pos = 0; + int rc; + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 + // needed when using _PySet_NextEntryRef + Py_BEGIN_CRITICAL_SECTION(val); +#endif /* Only one item is considered, as in typeof.py */ - if (!PySet_NextEntry(val, &pos, &item, &h)) { + rc = PySet_NextEntry(val, &pos, &item, &h); + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 13 + // needed when using _PySet_NextEntryRef + Py_END_CRITICAL_SECTION(); +#endif + + if (!rc) { /* Empty set */ PyErr_SetString(PyExc_ValueError, "cannot compute fingerprint of empty set");