Skip to content

Commit a6c49d1

Browse files
committed
Drop bespoke GILOnceCell implementation used to cache capsule API
While we do not strictly need all the capabilities and guarantees of PyO3's GILOnceCell, reducing the amount of open-coded synchronization seems like it is worth it to me. It also better centralizes GIL-based synchronization concerns in the PyO3 crate where alternative implementations better matching the Python implementation could be provided.
1 parent 1bbbad0 commit a6c49d1

File tree

2 files changed

+17
-44
lines changed

2 files changed

+17
-44
lines changed

src/npyffi/array.rs

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
//! The reason is that they would be re-exports of the `PyMem_Raw{Malloc,Realloc,Free}` functions from PyO3,
55
//! but those are not unconditionally exported, i.e. they are not available when using the limited Python C-API.
66
7-
use std::cell::Cell;
87
use std::os::raw::*;
9-
use std::ptr::null;
108

119
use libc::FILE;
12-
use pyo3::ffi::{self, PyObject, PyTypeObject};
10+
use pyo3::{
11+
ffi::{self, PyObject, PyTypeObject},
12+
once_cell::GILOnceCell,
13+
};
1314

1415
use crate::npyffi::*;
1516

@@ -34,34 +35,21 @@ const CAPSULE_NAME: &str = "_ARRAY_API";
3435
/// assert_eq!(array.readonly().as_slice().unwrap(), &[2, 3, 4]);
3536
/// })
3637
/// ```
37-
pub static PY_ARRAY_API: PyArrayAPI = PyArrayAPI::new();
38+
pub static PY_ARRAY_API: PyArrayAPI = PyArrayAPI(GILOnceCell::new());
3839

3940
/// See [PY_ARRAY_API] for more.
40-
pub struct PyArrayAPI {
41-
api: Cell<*const *const c_void>,
42-
}
41+
pub struct PyArrayAPI(GILOnceCell<*const *const c_void>);
4342

4443
unsafe impl Send for PyArrayAPI {}
4544

4645
unsafe impl Sync for PyArrayAPI {}
4746

4847
impl PyArrayAPI {
49-
const fn new() -> Self {
50-
Self {
51-
api: Cell::new(null()),
52-
}
53-
}
54-
#[cold]
55-
fn init(&self, py: Python) -> *const *const c_void {
56-
let api = get_numpy_api(py, MOD_NAME, CAPSULE_NAME);
57-
self.api.set(api);
58-
api
59-
}
6048
unsafe fn get(&self, py: Python, offset: isize) -> *const *const c_void {
61-
let mut api = self.api.get();
62-
if api.is_null() {
63-
api = self.init(py);
64-
}
49+
let api = self
50+
.0
51+
.get_or_init(py, || get_numpy_api(py, MOD_NAME, CAPSULE_NAME));
52+
6553
api.offset(offset)
6654
}
6755
}

src/npyffi/ufunc.rs

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
//! Low-Level binding for [UFunc API](https://numpy.org/doc/stable/reference/c-api/ufunc.html)
22
3-
use std::cell::Cell;
43
use std::os::raw::*;
5-
use std::ptr::null;
64

7-
use pyo3::ffi::PyObject;
5+
use pyo3::{ffi::PyObject, once_cell::GILOnceCell};
86

97
use crate::npyffi::*;
108

@@ -13,33 +11,20 @@ const CAPSULE_NAME: &str = "_UFUNC_API";
1311

1412
/// A global variable which stores a ['capsule'](https://docs.python.org/3/c-api/capsule.html)
1513
/// pointer to [Numpy UFunc API](https://numpy.org/doc/stable/reference/c-api/ufunc.html).
16-
pub static PY_UFUNC_API: PyUFuncAPI = PyUFuncAPI::new();
14+
pub static PY_UFUNC_API: PyUFuncAPI = PyUFuncAPI(GILOnceCell::new());
1715

18-
pub struct PyUFuncAPI {
19-
api: Cell<*const *const c_void>,
20-
}
16+
pub struct PyUFuncAPI(GILOnceCell<*const *const c_void>);
2117

2218
unsafe impl Send for PyUFuncAPI {}
2319

2420
unsafe impl Sync for PyUFuncAPI {}
2521

2622
impl PyUFuncAPI {
27-
const fn new() -> Self {
28-
Self {
29-
api: Cell::new(null()),
30-
}
31-
}
32-
#[cold]
33-
fn init(&self, py: Python) -> *const *const c_void {
34-
let api = get_numpy_api(py, MOD_NAME, CAPSULE_NAME);
35-
self.api.set(api);
36-
api
37-
}
3823
unsafe fn get(&self, py: Python, offset: isize) -> *const *const c_void {
39-
let mut api = self.api.get();
40-
if api.is_null() {
41-
api = self.init(py);
42-
}
24+
let api = self
25+
.0
26+
.get_or_init(py, || get_numpy_api(py, MOD_NAME, CAPSULE_NAME));
27+
4328
api.offset(offset)
4429
}
4530
}

0 commit comments

Comments
 (0)