Skip to content

Fix passing of extra arguments to py_safe_call_once#7585

Merged
da-woods merged 2 commits intocython:masterfrom
da-woods:py-safe-call-once-arg-passing
Apr 1, 2026
Merged

Fix passing of extra arguments to py_safe_call_once#7585
da-woods merged 2 commits intocython:masterfrom
da-woods:py-safe-call-once-arg-passing

Conversation

@da-woods
Copy link
Copy Markdown
Contributor

It wasn't compiling because I wasn't using std::forward correctly - they should be consistently forwarding references.

Being able to pass arguments is useful because it can avoid the need for a closure in some cases.

It wasn't compiling because I wasn't using `std::forward`
correctly - they should be consistently forwarding references.

Being able to pass arguments is useful because it can avoid the
need for a closure in some cases.
@ngoldbaum
Copy link
Copy Markdown
Contributor

I can confirm this lets me remove the wrapper object I added in scipy/scipy#24799:

diff --git a/scipy/spatial/_ckdtree.pyx b/scipy/spatial/_ckdtree.pyx
index 74de92ef58..31edd4acda 100644
--- a/scipy/spatial/_ckdtree.pyx
+++ b/scipy/spatial/_ckdtree.pyx
@@ -14,7 +14,7 @@ from scipy._lib._util import copy_if_needed
 cimport numpy as np
 
 from cpython.mem cimport PyMem_Malloc, PyMem_Free
-from libcpp.mutex cimport py_safe_call_object_once, py_safe_once_flag
+from libcpp.mutex cimport py_safe_call_once, py_safe_once_flag
 from libcpp.vector cimport vector
 from libcpp cimport bool
 from libc.math cimport isinf, INFINITY
@@ -408,17 +408,11 @@ cdef np.intp_t get_num_workers(workers: object, kwargs: dict) except -1:
     return n
 
 
-cdef class call_once_wrapper:
-    cdef:
-        cKDTree tree
-
-    def __cinit__(call_once_wrapper self, cKDTree tree):
-        self.tree = tree
-
-    def __call__(self):
-        n = cKDTreeNode()
-        n._setup(self.tree, node=self.tree.cself.ctree, level=0)
-        self.tree._python_tree = n
+cdef void init_pytree(void *void_tree):
+    cdef cKDTree tree = <cKDTree>void_tree
+    n = cKDTreeNode()
+    n._setup(tree, node=tree.cself.ctree, level=0)
+    tree._python_tree = n
 
 
 # Main cKDTree class
@@ -558,9 +552,9 @@ cdef class cKDTree:
         def __get__(cKDTree self):
             cdef cKDTreeNode n
             cdef ckdtree *cself = self.cself
-            cdef call_once_wrapper wrapper
-            wrapper = call_once_wrapper(self)
-            py_safe_call_object_once(self.flag, wrapper)
+            # cast to void* is safe because it is either used to construct the
+            # python tree and then discarded or immediately discarded
+            py_safe_call_once(self.flag, init_pytree, <void *>self)
             return self._python_tree
 
     def __cinit__(cKDTree self):

@da-woods
Copy link
Copy Markdown
Contributor Author

Try doing

cdef void* self_v = <void*>self
py_safe_call_once(self.flag, init_pytree, self_v)

I think that might work with the current Cython version so wouldn't need to wait for the bug fix. Not an absolute promise though (this is typed on my phone without an easy way to test it)

@ngoldbaum
Copy link
Copy Markdown
Contributor

Not an absolute promise though (this is typed on my phone without an easy way to test it)

I appreciate your phone programming! It does build.

@da-woods da-woods added this to the 3.2.5 milestone Apr 1, 2026
@da-woods da-woods merged commit 639fdfa into cython:master Apr 1, 2026
79 checks passed
da-woods added a commit that referenced this pull request Apr 1, 2026
It wasn't compiling because I wasn't using `std::forward` correctly -
they should be consistently forwarding references.

Being able to pass arguments is useful because it can avoid the need for
a closure in some cases.
@da-woods
Copy link
Copy Markdown
Contributor Author

da-woods commented Apr 1, 2026

3.2.x commit d7d6ae5

@da-woods da-woods deleted the py-safe-call-once-arg-passing branch April 1, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants