@@ -102,19 +102,17 @@ impl<'py> PyBitGeneratorMethods<'py> for Bound<'py, PyBitGenerator> {
102
102
fn lock ( & self ) -> PyResult < PyBitGeneratorGuard > {
103
103
let capsule = self . getattr ( "capsule" ) ?. downcast_into :: < PyCapsule > ( ) ?;
104
104
let lock = self . getattr ( "lock" ) ?;
105
+ // we’re holding the GIL, so there’s no race condition checking the lock and acquiring it later.
105
106
if lock. call_method0 ( "locked" ) ?. extract ( ) ? {
106
107
return Err ( PyRuntimeError :: new_err ( "BitGenerator is already locked" ) ) ;
107
108
}
108
109
lock. call_method0 ( "acquire" ) ?;
109
110
110
111
assert_eq ! ( capsule. name( ) ?, Some ( c"BitGenerator" ) ) ;
111
112
let ptr = capsule. pointer ( ) as * mut npy_bitgen ;
112
- let non_null = match NonNull :: new ( ptr) {
113
- Some ( non_null) => non_null,
114
- None => {
115
- lock. call_method0 ( "release" ) ?;
116
- return Err ( PyRuntimeError :: new_err ( "Invalid BitGenerator capsule" ) ) ;
117
- }
113
+ let Some ( non_null) = NonNull :: new ( ptr) else {
114
+ lock. call_method0 ( "release" ) ?;
115
+ return Err ( PyRuntimeError :: new_err ( "Invalid BitGenerator capsule" ) ) ;
118
116
} ;
119
117
Ok ( PyBitGeneratorGuard {
120
118
raw_bitgen : non_null,
@@ -140,8 +138,8 @@ pub struct PyBitGeneratorGuard {
140
138
lock : Py < PyAny > ,
141
139
}
142
140
143
- // SAFETY: we can ’t have public APIs that access the Python objects,
144
- // only the `raw_bitgen` pointer .
141
+ // SAFETY: 1. We don ’t hold the GIL, so we can’t access the Python objects.
142
+ // 2. We only access `raw_bitgen` from `&mut self`, which protects it from parallel access .
145
143
unsafe impl Send for PyBitGeneratorGuard { }
146
144
147
145
impl Drop for PyBitGeneratorGuard {
@@ -154,11 +152,10 @@ impl Drop for PyBitGeneratorGuard {
154
152
}
155
153
}
156
154
157
- // SAFETY: We hold the `BitGenerator.lock`,
158
- // so nothing apart from us is allowed to change its state .
155
+ // SAFETY: 1. We hold the `BitGenerator.lock`, so nothing apart from us is allowed to change its state.
156
+ // 2. We hold the `BitGenerator.capsule`, so it can’t be deallocated .
159
157
impl < ' py > PyBitGeneratorGuard {
160
158
/// Release the lock, allowing for checking for errors.
161
- #[ allow( dead_code) ]
162
159
pub fn try_release ( self , py : Python < ' py > ) -> PyResult < ( ) > {
163
160
self . lock . bind ( py) . call_method0 ( "release" ) ?;
164
161
Ok ( ( ) )
@@ -249,7 +246,7 @@ mod tests {
249
246
let ( _n_threads, chunk_size) = arr. dims ( ) . into_pattern ( ) ;
250
247
let slice = arr. as_slice_mut ( ) ?;
251
248
252
- Python :: allow_threads ( py , || {
249
+ py . allow_threads ( || {
253
250
std:: thread:: scope ( |s| {
254
251
for chunk in slice. chunks_exact_mut ( chunk_size) {
255
252
let bitgen = Arc :: clone ( & bitgen) ;
@@ -260,6 +257,8 @@ mod tests {
260
257
}
261
258
} )
262
259
} ) ;
260
+
261
+ std:: mem:: drop ( bitgen) ;
263
262
Ok ( ( ) )
264
263
} )
265
264
}
0 commit comments