@@ -102,19 +102,17 @@ impl<'py> PyBitGeneratorMethods<'py> for Bound<'py, PyBitGenerator> {
102102 fn lock ( & self ) -> PyResult < PyBitGeneratorGuard > {
103103 let capsule = self . getattr ( "capsule" ) ?. downcast_into :: < PyCapsule > ( ) ?;
104104 let lock = self . getattr ( "lock" ) ?;
105+ // we’re holding the GIL, so there’s no race condition checking the lock and acquiring it later.
105106 if lock. call_method0 ( "locked" ) ?. extract ( ) ? {
106107 return Err ( PyRuntimeError :: new_err ( "BitGenerator is already locked" ) ) ;
107108 }
108109 lock. call_method0 ( "acquire" ) ?;
109110
110111 assert_eq ! ( capsule. name( ) ?, Some ( c"BitGenerator" ) ) ;
111112 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" ) ) ;
118116 } ;
119117 Ok ( PyBitGeneratorGuard {
120118 raw_bitgen : non_null,
@@ -140,8 +138,8 @@ pub struct PyBitGeneratorGuard {
140138 lock : Py < PyAny > ,
141139}
142140
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 .
145143unsafe impl Send for PyBitGeneratorGuard { }
146144
147145impl Drop for PyBitGeneratorGuard {
@@ -154,11 +152,10 @@ impl Drop for PyBitGeneratorGuard {
154152 }
155153}
156154
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 .
159157impl < ' py > PyBitGeneratorGuard {
160158 /// Release the lock, allowing for checking for errors.
161- #[ allow( dead_code) ]
162159 pub fn try_release ( self , py : Python < ' py > ) -> PyResult < ( ) > {
163160 self . lock . bind ( py) . call_method0 ( "release" ) ?;
164161 Ok ( ( ) )
@@ -249,7 +246,7 @@ mod tests {
249246 let ( _n_threads, chunk_size) = arr. dims ( ) . into_pattern ( ) ;
250247 let slice = arr. as_slice_mut ( ) ?;
251248
252- Python :: allow_threads ( py , || {
249+ py . allow_threads ( || {
253250 std:: thread:: scope ( |s| {
254251 for chunk in slice. chunks_exact_mut ( chunk_size) {
255252 let bitgen = Arc :: clone ( & bitgen) ;
@@ -260,6 +257,8 @@ mod tests {
260257 }
261258 } )
262259 } ) ;
260+
261+ std:: mem:: drop ( bitgen) ;
263262 Ok ( ( ) )
264263 } )
265264 }
0 commit comments