1use crate::{
13 internal::state::SuspendAttach,
14 sealed::Sealed,
15 types::PyString,
16 Bound, Py, Python,
17};
18use std::{
19 cell::UnsafeCell,
20 marker::PhantomData,
21 mem::MaybeUninit,
22 sync::{Once, OnceState},
23};
24
25pub mod critical_section;
26pub(crate) mod once_lock;
27
28pub use self::once_lock::PyOnceLock;
29
30pub(crate) struct GILOnceCell<T> {
32 once: Once,
33 data: UnsafeCell<MaybeUninit<T>>,
34
35 _marker: PhantomData<T>,
57}
58
59impl<T> Default for GILOnceCell<T> {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65unsafe impl<T: Send + Sync> Sync for GILOnceCell<T> {}
69unsafe impl<T: Send> Send for GILOnceCell<T> {}
70
71impl<T> GILOnceCell<T> {
72 pub const fn new() -> Self {
74 Self {
75 once: Once::new(),
76 data: UnsafeCell::new(MaybeUninit::uninit()),
77 _marker: PhantomData,
78 }
79 }
80
81 #[inline]
83 pub fn get(&self, _py: Python<'_>) -> Option<&T> {
84 if self.once.is_completed() {
85 Some(unsafe { (*self.data.get()).assume_init_ref() })
87 } else {
88 None
89 }
90 }
91
92 #[inline]
97 pub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
98 where
99 F: FnOnce() -> Result<T, E>,
100 {
101 if let Some(value) = self.get(py) {
102 return Ok(value);
103 }
104
105 self.init(py, f)
106 }
107
108 #[cold]
109 fn init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
110 where
111 F: FnOnce() -> Result<T, E>,
112 {
113 let value = f()?;
121 let _ = self.set(py, value);
122
123 Ok(self.get(py).unwrap())
124 }
125
126 pub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T> {
131 let mut value = Some(value);
132 self.once.call_once_force(|_| {
136 unsafe {
139 (*self.data.get()).write(value.take().unwrap());
141 }
142 });
143
144 match value {
145 Some(value) => Err(value),
147 None => Ok(()),
148 }
149 }
150}
151
152impl<T> Drop for GILOnceCell<T> {
153 fn drop(&mut self) {
154 if self.once.is_completed() {
155 unsafe { MaybeUninit::assume_init_drop(self.data.get_mut()) }
157 }
158 }
159}
160
161#[macro_export]
199macro_rules! intern {
200 ($py: expr, $text: expr) => {{
201 static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text);
202 INTERNED.get($py)
203 }};
204}
205
206#[doc(hidden)]
208pub struct Interned(&'static str, PyOnceLock<Py<PyString>>);
209
210impl Interned {
211 pub const fn new(value: &'static str) -> Self {
213 Interned(value, PyOnceLock::new())
214 }
215
216 #[inline]
218 pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> {
219 self.1
220 .get_or_init(py, || PyString::intern(py, self.0).into())
221 .bind(py)
222 }
223}
224
225pub trait OnceExt: Sealed {
228 type OnceState;
230
231 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce());
234
235 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&Self::OnceState));
238}
239
240pub trait OnceLockExt<T>: once_lock_ext_sealed::Sealed {
243 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
253 where
254 F: FnOnce() -> T;
255}
256
257pub trait MutexExt<T>: Sealed {
260 type LockResult<'a>
262 where
263 Self: 'a;
264
265 fn lock_py_attached(&self, py: Python<'_>) -> Self::LockResult<'_>;
273}
274
275pub trait RwLockExt<T>: rwlock_ext_sealed::Sealed {
278 type ReadLockResult<'a>
280 where
281 Self: 'a;
282
283 type WriteLockResult<'a>
285 where
286 Self: 'a;
287
288 fn read_py_attached(&self, py: Python<'_>) -> Self::ReadLockResult<'_>;
297
298 fn write_py_attached(&self, py: Python<'_>) -> Self::WriteLockResult<'_>;
307}
308
309impl OnceExt for Once {
310 type OnceState = OnceState;
311
312 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce()) {
313 if self.is_completed() {
314 return;
315 }
316
317 init_once_py_attached(self, py, f)
318 }
319
320 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&OnceState)) {
321 if self.is_completed() {
322 return;
323 }
324
325 init_once_force_py_attached(self, py, f);
326 }
327}
328
329#[cfg(feature = "parking_lot")]
330impl OnceExt for parking_lot::Once {
331 type OnceState = parking_lot::OnceState;
332
333 fn call_once_py_attached(&self, _py: Python<'_>, f: impl FnOnce()) {
334 if self.state().done() {
335 return;
336 }
337
338 let ts_guard = unsafe { SuspendAttach::new() };
339
340 self.call_once(move || {
341 drop(ts_guard);
342 f();
343 });
344 }
345
346 fn call_once_force_py_attached(
347 &self,
348 _py: Python<'_>,
349 f: impl FnOnce(&parking_lot::OnceState),
350 ) {
351 if self.state().done() {
352 return;
353 }
354
355 let ts_guard = unsafe { SuspendAttach::new() };
356
357 self.call_once_force(move |state| {
358 drop(ts_guard);
359 f(&state);
360 });
361 }
362}
363
364impl<T> OnceLockExt<T> for std::sync::OnceLock<T> {
365 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
366 where
367 F: FnOnce() -> T,
368 {
369 self.get()
371 .unwrap_or_else(|| init_once_lock_py_attached(self, py, f))
372 }
373}
374
375impl<T> MutexExt<T> for std::sync::Mutex<T> {
376 type LockResult<'a>
377 = std::sync::LockResult<std::sync::MutexGuard<'a, T>>
378 where
379 Self: 'a;
380
381 fn lock_py_attached(
382 &self,
383 _py: Python<'_>,
384 ) -> std::sync::LockResult<std::sync::MutexGuard<'_, T>> {
385 match self.try_lock() {
390 Ok(inner) => return Ok(inner),
391 Err(std::sync::TryLockError::Poisoned(inner)) => {
392 return std::sync::LockResult::Err(inner)
393 }
394 Err(std::sync::TryLockError::WouldBlock) => {}
395 }
396 let ts_guard = unsafe { SuspendAttach::new() };
400 let res = self.lock();
401 drop(ts_guard);
402 res
403 }
404}
405
406#[cfg(feature = "lock_api")]
407impl<R: lock_api::RawMutex, T> MutexExt<T> for lock_api::Mutex<R, T> {
408 type LockResult<'a>
409 = lock_api::MutexGuard<'a, R, T>
410 where
411 Self: 'a;
412
413 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::MutexGuard<'_, R, T> {
414 if let Some(guard) = self.try_lock() {
415 return guard;
416 }
417
418 let ts_guard = unsafe { SuspendAttach::new() };
419 let res = self.lock();
420 drop(ts_guard);
421 res
422 }
423}
424
425#[cfg(feature = "arc_lock")]
426impl<R, T> MutexExt<T> for std::sync::Arc<lock_api::Mutex<R, T>>
427where
428 R: lock_api::RawMutex,
429{
430 type LockResult<'a>
431 = lock_api::ArcMutexGuard<R, T>
432 where
433 Self: 'a;
434
435 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcMutexGuard<R, T> {
436 if let Some(guard) = self.try_lock_arc() {
437 return guard;
438 }
439
440 let ts_guard = unsafe { SuspendAttach::new() };
441 let res = self.lock_arc();
442 drop(ts_guard);
443 res
444 }
445}
446
447#[cfg(feature = "lock_api")]
448impl<R, G, T> MutexExt<T> for lock_api::ReentrantMutex<R, G, T>
449where
450 R: lock_api::RawMutex,
451 G: lock_api::GetThreadId,
452{
453 type LockResult<'a>
454 = lock_api::ReentrantMutexGuard<'a, R, G, T>
455 where
456 Self: 'a;
457
458 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ReentrantMutexGuard<'_, R, G, T> {
459 if let Some(guard) = self.try_lock() {
460 return guard;
461 }
462
463 let ts_guard = unsafe { SuspendAttach::new() };
464 let res = self.lock();
465 drop(ts_guard);
466 res
467 }
468}
469
470#[cfg(feature = "arc_lock")]
471impl<R, G, T> MutexExt<T> for std::sync::Arc<lock_api::ReentrantMutex<R, G, T>>
472where
473 R: lock_api::RawMutex,
474 G: lock_api::GetThreadId,
475{
476 type LockResult<'a>
477 = lock_api::ArcReentrantMutexGuard<R, G, T>
478 where
479 Self: 'a;
480
481 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcReentrantMutexGuard<R, G, T> {
482 if let Some(guard) = self.try_lock_arc() {
483 return guard;
484 }
485
486 let ts_guard = unsafe { SuspendAttach::new() };
487 let res = self.lock_arc();
488 drop(ts_guard);
489 res
490 }
491}
492
493impl<T> RwLockExt<T> for std::sync::RwLock<T> {
494 type ReadLockResult<'a>
495 = std::sync::LockResult<std::sync::RwLockReadGuard<'a, T>>
496 where
497 Self: 'a;
498
499 type WriteLockResult<'a>
500 = std::sync::LockResult<std::sync::RwLockWriteGuard<'a, T>>
501 where
502 Self: 'a;
503
504 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
505 match self.try_read() {
510 Ok(inner) => return Ok(inner),
511 Err(std::sync::TryLockError::Poisoned(inner)) => {
512 return std::sync::LockResult::Err(inner)
513 }
514 Err(std::sync::TryLockError::WouldBlock) => {}
515 }
516
517 let ts_guard = unsafe { SuspendAttach::new() };
521
522 let res = self.read();
523 drop(ts_guard);
524 res
525 }
526
527 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
528 match self.try_write() {
533 Ok(inner) => return Ok(inner),
534 Err(std::sync::TryLockError::Poisoned(inner)) => {
535 return std::sync::LockResult::Err(inner)
536 }
537 Err(std::sync::TryLockError::WouldBlock) => {}
538 }
539
540 let ts_guard = unsafe { SuspendAttach::new() };
544
545 let res = self.write();
546 drop(ts_guard);
547 res
548 }
549}
550
551#[cfg(feature = "lock_api")]
552impl<R: lock_api::RawRwLock, T> RwLockExt<T> for lock_api::RwLock<R, T> {
553 type ReadLockResult<'a>
554 = lock_api::RwLockReadGuard<'a, R, T>
555 where
556 Self: 'a;
557
558 type WriteLockResult<'a>
559 = lock_api::RwLockWriteGuard<'a, R, T>
560 where
561 Self: 'a;
562
563 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
564 if let Some(guard) = self.try_read() {
565 return guard;
566 }
567
568 let ts_guard = unsafe { SuspendAttach::new() };
569 let res = self.read();
570 drop(ts_guard);
571 res
572 }
573
574 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
575 if let Some(guard) = self.try_write() {
576 return guard;
577 }
578
579 let ts_guard = unsafe { SuspendAttach::new() };
580 let res = self.write();
581 drop(ts_guard);
582 res
583 }
584}
585
586#[cfg(feature = "arc_lock")]
587impl<R, T> RwLockExt<T> for std::sync::Arc<lock_api::RwLock<R, T>>
588where
589 R: lock_api::RawRwLock,
590{
591 type ReadLockResult<'a>
592 = lock_api::ArcRwLockReadGuard<R, T>
593 where
594 Self: 'a;
595
596 type WriteLockResult<'a>
597 = lock_api::ArcRwLockWriteGuard<R, T>
598 where
599 Self: 'a;
600
601 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
602 if let Some(guard) = self.try_read_arc() {
603 return guard;
604 }
605
606 let ts_guard = unsafe { SuspendAttach::new() };
607 let res = self.read_arc();
608 drop(ts_guard);
609 res
610 }
611
612 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
613 if let Some(guard) = self.try_write_arc() {
614 return guard;
615 }
616
617 let ts_guard = unsafe { SuspendAttach::new() };
618 let res = self.write_arc();
619 drop(ts_guard);
620 res
621 }
622}
623
624#[cold]
625fn init_once_py_attached<F, T>(once: &Once, _py: Python<'_>, f: F)
626where
627 F: FnOnce() -> T,
628{
629 let ts_guard = unsafe { SuspendAttach::new() };
633
634 once.call_once(move || {
635 drop(ts_guard);
636 f();
637 });
638}
639
640#[cold]
641fn init_once_force_py_attached<F, T>(once: &Once, _py: Python<'_>, f: F)
642where
643 F: FnOnce(&OnceState) -> T,
644{
645 let ts_guard = unsafe { SuspendAttach::new() };
649
650 once.call_once_force(move |state| {
651 drop(ts_guard);
652 f(state);
653 });
654}
655
656#[cold]
657fn init_once_lock_py_attached<'a, F, T>(
658 lock: &'a std::sync::OnceLock<T>,
659 _py: Python<'_>,
660 f: F,
661) -> &'a T
662where
663 F: FnOnce() -> T,
664{
665 let ts_guard = unsafe { SuspendAttach::new() };
669
670 let value = lock.get_or_init(move || {
673 drop(ts_guard);
674 f()
675 });
676
677 value
678}
679
680mod once_lock_ext_sealed {
681 pub trait Sealed {}
682 impl<T> Sealed for std::sync::OnceLock<T> {}
683}
684
685mod rwlock_ext_sealed {
686 pub trait Sealed {}
687 impl<T> Sealed for std::sync::RwLock<T> {}
688 #[cfg(feature = "lock_api")]
689 impl<R, T> Sealed for lock_api::RwLock<R, T> {}
690 #[cfg(feature = "arc_lock")]
691 impl<R, T> Sealed for std::sync::Arc<lock_api::RwLock<R, T>> {}
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697
698 use crate::types::{PyAnyMethods, PyDict, PyDictMethods};
699 #[cfg(not(target_arch = "wasm32"))]
700 #[cfg(feature = "macros")]
701 use std::sync::atomic::{AtomicBool, Ordering};
702 #[cfg(not(target_arch = "wasm32"))]
703 #[cfg(feature = "macros")]
704 use std::sync::Barrier;
705 #[cfg(not(target_arch = "wasm32"))]
706 use std::sync::Mutex;
707
708 #[cfg(not(target_arch = "wasm32"))]
709 #[cfg(feature = "macros")]
710 #[crate::pyclass(crate = "crate")]
711 struct BoolWrapper(AtomicBool);
712
713 #[test]
714 fn test_intern() {
715 Python::attach(|py| {
716 let foo1 = "foo";
717 let foo2 = intern!(py, "foo");
718 let foo3 = intern!(py, stringify!(foo));
719
720 let dict = PyDict::new(py);
721 dict.set_item(foo1, 42_usize).unwrap();
722 assert!(dict.contains(foo2).unwrap());
723 assert_eq!(
724 dict.get_item(foo3)
725 .unwrap()
726 .unwrap()
727 .extract::<usize>()
728 .unwrap(),
729 42
730 );
731 });
732 }
733
734 #[test]
735 fn test_once_cell() {
736 Python::attach(|py| {
737 let cell = GILOnceCell::new();
738
739 assert!(cell.get(py).is_none());
740
741 assert_eq!(cell.get_or_try_init(py, || Err(5)), Err(5));
742 assert!(cell.get(py).is_none());
743
744 assert_eq!(cell.get_or_try_init(py, || Ok::<_, ()>(2)), Ok(&2));
745 assert_eq!(cell.get(py), Some(&2));
746
747 assert_eq!(cell.get_or_try_init(py, || Err(5)), Ok(&2));
748 })
749 }
750
751 #[test]
752 fn test_once_cell_drop() {
753 #[derive(Debug)]
754 struct RecordDrop<'a>(&'a mut bool);
755
756 impl Drop for RecordDrop<'_> {
757 fn drop(&mut self) {
758 *self.0 = true;
759 }
760 }
761
762 Python::attach(|py| {
763 let mut dropped = false;
764 let cell = GILOnceCell::new();
765 cell.set(py, RecordDrop(&mut dropped)).unwrap();
766 let drop_container = cell.get(py).unwrap();
767
768 assert!(!*drop_container.0);
769 drop(cell);
770 assert!(dropped);
771 });
772 }
773
774 #[test]
775 #[cfg(not(target_arch = "wasm32"))] fn test_once_ext() {
777 macro_rules! test_once {
778 ($once:expr, $is_poisoned:expr) => {{
779 let init = $once;
781 std::thread::scope(|s| {
782 let handle = s.spawn(|| {
784 Python::attach(|py| {
785 init.call_once_py_attached(py, || panic!());
786 })
787 });
788 assert!(handle.join().is_err());
789
790 let handle = s.spawn(|| {
792 Python::attach(|py| {
793 init.call_once_py_attached(py, || {});
794 });
795 });
796
797 assert!(handle.join().is_err());
798
799 Python::attach(|py| {
801 init.call_once_force_py_attached(py, |state| {
802 assert!($is_poisoned(state.clone()));
803 });
804
805 init.call_once_py_attached(py, || {});
807 });
808
809 Python::attach(|py| init.call_once_force_py_attached(py, |_| panic!()));
811 });
812 }};
813 }
814
815 test_once!(Once::new(), OnceState::is_poisoned);
816 #[cfg(feature = "parking_lot")]
817 test_once!(parking_lot::Once::new(), parking_lot::OnceState::poisoned);
818 }
819
820 #[cfg(not(target_arch = "wasm32"))] #[test]
822 fn test_once_lock_ext() {
823 let cell = std::sync::OnceLock::new();
824 std::thread::scope(|s| {
825 assert!(cell.get().is_none());
826
827 s.spawn(|| {
828 Python::attach(|py| {
829 assert_eq!(*cell.get_or_init_py_attached(py, || 12345), 12345);
830 });
831 });
832 });
833 assert_eq!(cell.get(), Some(&12345));
834 }
835
836 #[cfg(feature = "macros")]
837 #[cfg(not(target_arch = "wasm32"))] #[test]
839 fn test_mutex_ext() {
840 let barrier = Barrier::new(2);
841
842 let mutex = Python::attach(|py| -> Mutex<Py<BoolWrapper>> {
843 Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
844 });
845
846 std::thread::scope(|s| {
847 s.spawn(|| {
848 Python::attach(|py| {
849 let b = mutex.lock_py_attached(py).unwrap();
850 barrier.wait();
851 std::thread::sleep(std::time::Duration::from_millis(10));
853 (*b).bind(py).borrow().0.store(true, Ordering::Release);
854 drop(b);
855 });
856 });
857 s.spawn(|| {
858 barrier.wait();
859 Python::attach(|py| {
860 let b = mutex.lock_py_attached(py).unwrap();
862 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
863 });
864 });
865 });
866 }
867
868 #[cfg(feature = "macros")]
869 #[cfg(all(
870 any(feature = "parking_lot", feature = "lock_api"),
871 not(target_arch = "wasm32") ))]
873 #[test]
874 fn test_parking_lot_mutex_ext() {
875 macro_rules! test_mutex {
876 ($guard:ty ,$mutex:stmt) => {{
877 let barrier = Barrier::new(2);
878
879 let mutex = Python::attach({ $mutex });
880
881 std::thread::scope(|s| {
882 s.spawn(|| {
883 Python::attach(|py| {
884 let b: $guard = mutex.lock_py_attached(py);
885 barrier.wait();
886 std::thread::sleep(std::time::Duration::from_millis(10));
888 (*b).bind(py).borrow().0.store(true, Ordering::Release);
889 drop(b);
890 });
891 });
892 s.spawn(|| {
893 barrier.wait();
894 Python::attach(|py| {
895 let b: $guard = mutex.lock_py_attached(py);
897 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
898 });
899 });
900 });
901 }};
902 }
903
904 test_mutex!(parking_lot::MutexGuard<'_, _>, |py| {
905 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
906 });
907
908 test_mutex!(parking_lot::ReentrantMutexGuard<'_, _>, |py| {
909 parking_lot::ReentrantMutex::new(
910 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
911 )
912 });
913
914 #[cfg(feature = "arc_lock")]
915 test_mutex!(parking_lot::ArcMutexGuard<_, _>, |py| {
916 let mutex =
917 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
918 std::sync::Arc::new(mutex)
919 });
920
921 #[cfg(feature = "arc_lock")]
922 test_mutex!(parking_lot::ArcReentrantMutexGuard<_, _, _>, |py| {
923 let mutex =
924 parking_lot::ReentrantMutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
925 std::sync::Arc::new(mutex)
926 });
927 }
928
929 #[cfg(not(target_arch = "wasm32"))] #[test]
931 fn test_mutex_ext_poison() {
932 let mutex = Mutex::new(42);
933
934 std::thread::scope(|s| {
935 let lock_result = s.spawn(|| {
936 Python::attach(|py| {
937 let _unused = mutex.lock_py_attached(py);
938 panic!();
939 });
940 });
941 assert!(lock_result.join().is_err());
942 assert!(mutex.is_poisoned());
943 });
944 let guard = Python::attach(|py| {
945 match mutex.lock_py_attached(py) {
947 Ok(guard) => guard,
948 Err(poisoned) => poisoned.into_inner(),
949 }
950 });
951 assert_eq!(*guard, 42);
952 }
953
954 #[cfg(feature = "macros")]
955 #[cfg(not(target_arch = "wasm32"))] #[test]
957 fn test_rwlock_ext_writer_blocks_reader() {
958 use std::sync::RwLock;
959
960 let barrier = Barrier::new(2);
961
962 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
963 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
964 });
965
966 std::thread::scope(|s| {
967 s.spawn(|| {
968 Python::attach(|py| {
969 let b = rwlock.write_py_attached(py).unwrap();
970 barrier.wait();
971 std::thread::sleep(std::time::Duration::from_millis(10));
973 (*b).bind(py).borrow().0.store(true, Ordering::Release);
974 drop(b);
975 });
976 });
977 s.spawn(|| {
978 barrier.wait();
979 Python::attach(|py| {
980 let b = rwlock.read_py_attached(py).unwrap();
982 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
983 });
984 });
985 });
986 }
987
988 #[cfg(feature = "macros")]
989 #[cfg(not(target_arch = "wasm32"))] #[test]
991 fn test_rwlock_ext_reader_blocks_writer() {
992 use std::sync::RwLock;
993
994 let barrier = Barrier::new(2);
995
996 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
997 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
998 });
999
1000 std::thread::scope(|s| {
1001 s.spawn(|| {
1002 Python::attach(|py| {
1003 let b = rwlock.read_py_attached(py).unwrap();
1004 barrier.wait();
1005
1006 std::thread::sleep(std::time::Duration::from_millis(10));
1008
1009 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire));
1012 });
1013 });
1014 s.spawn(|| {
1015 barrier.wait();
1016 Python::attach(|py| {
1017 let b = rwlock.write_py_attached(py).unwrap();
1019 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1020 drop(b);
1021 });
1022 });
1023 });
1024
1025 Python::attach(|py| {
1027 let b = rwlock.read_py_attached(py).unwrap();
1028 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1029 drop(b);
1030 });
1031 }
1032
1033 #[cfg(feature = "macros")]
1034 #[cfg(all(
1035 any(feature = "parking_lot", feature = "lock_api"),
1036 not(target_arch = "wasm32") ))]
1038 #[test]
1039 fn test_parking_lot_rwlock_ext_writer_blocks_reader() {
1040 macro_rules! test_rwlock {
1041 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1042 let barrier = Barrier::new(2);
1043
1044 let rwlock = Python::attach({ $rwlock });
1045
1046 std::thread::scope(|s| {
1047 s.spawn(|| {
1048 Python::attach(|py| {
1049 let b: $write_guard = rwlock.write_py_attached(py);
1050 barrier.wait();
1051 std::thread::sleep(std::time::Duration::from_millis(10));
1053 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1054 drop(b);
1055 });
1056 });
1057 s.spawn(|| {
1058 barrier.wait();
1059 Python::attach(|py| {
1060 let b: $read_guard = rwlock.read_py_attached(py);
1062 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1063 });
1064 });
1065 });
1066 }};
1067 }
1068
1069 test_rwlock!(
1070 parking_lot::RwLockWriteGuard<'_, _>,
1071 parking_lot::RwLockReadGuard<'_, _>,
1072 |py| {
1073 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1074 }
1075 );
1076
1077 #[cfg(feature = "arc_lock")]
1078 test_rwlock!(
1079 parking_lot::ArcRwLockWriteGuard<_, _>,
1080 parking_lot::ArcRwLockReadGuard<_, _>,
1081 |py| {
1082 let rwlock = parking_lot::RwLock::new(
1083 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1084 );
1085 std::sync::Arc::new(rwlock)
1086 }
1087 );
1088 }
1089
1090 #[cfg(feature = "macros")]
1091 #[cfg(all(
1092 any(feature = "parking_lot", feature = "lock_api"),
1093 not(target_arch = "wasm32") ))]
1095 #[test]
1096 fn test_parking_lot_rwlock_ext_reader_blocks_writer() {
1097 macro_rules! test_rwlock {
1098 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1099 let barrier = Barrier::new(2);
1100
1101 let rwlock = Python::attach({ $rwlock });
1102
1103 std::thread::scope(|s| {
1104 s.spawn(|| {
1105 Python::attach(|py| {
1106 let b: $read_guard = rwlock.read_py_attached(py);
1107 barrier.wait();
1108
1109 std::thread::sleep(std::time::Duration::from_millis(10));
1111
1112 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire)); (*b).bind(py).borrow().0.store(true, Ordering::Release);
1115
1116 drop(b);
1117 });
1118 });
1119 s.spawn(|| {
1120 barrier.wait();
1121 Python::attach(|py| {
1122 let b: $write_guard = rwlock.write_py_attached(py);
1124 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1125 });
1126 });
1127 });
1128
1129 Python::attach(|py| {
1131 let b: $read_guard = rwlock.read_py_attached(py);
1132 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1133 drop(b);
1134 });
1135 }};
1136 }
1137
1138 test_rwlock!(
1139 parking_lot::RwLockWriteGuard<'_, _>,
1140 parking_lot::RwLockReadGuard<'_, _>,
1141 |py| {
1142 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1143 }
1144 );
1145
1146 #[cfg(feature = "arc_lock")]
1147 test_rwlock!(
1148 parking_lot::ArcRwLockWriteGuard<_, _>,
1149 parking_lot::ArcRwLockReadGuard<_, _>,
1150 |py| {
1151 let rwlock = parking_lot::RwLock::new(
1152 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1153 );
1154 std::sync::Arc::new(rwlock)
1155 }
1156 );
1157 }
1158
1159 #[cfg(not(target_arch = "wasm32"))] #[test]
1161 fn test_rwlock_ext_poison() {
1162 use std::sync::RwLock;
1163
1164 let rwlock = RwLock::new(42);
1165
1166 std::thread::scope(|s| {
1167 let lock_result = s.spawn(|| {
1168 Python::attach(|py| {
1169 let _unused = rwlock.write_py_attached(py);
1170 panic!();
1171 });
1172 });
1173 assert!(lock_result.join().is_err());
1174 assert!(rwlock.is_poisoned());
1175 Python::attach(|py| {
1176 assert!(rwlock.read_py_attached(py).is_err());
1177 assert!(rwlock.write_py_attached(py).is_err());
1178 });
1179 });
1180 Python::attach(|py| {
1181 let guard = rwlock.write_py_attached(py).unwrap_err().into_inner();
1183 assert_eq!(*guard, 42);
1184 });
1185 }
1186}