1use crate::{
13 internal::state::SuspendAttach,
14 sealed::Sealed,
15 types::{any::PyAnyMethods, PyAny, PyString},
16 Bound, Py, PyResult, PyTypeCheck, 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
28#[cfg(not(Py_GIL_DISABLED))]
29use crate::PyVisit;
30
31#[deprecated(
33 since = "0.28.0",
34 note = "use pyo3::sync::critical_section::with_critical_section instead"
35)]
36pub fn with_critical_section<F, R>(object: &Bound<'_, PyAny>, f: F) -> R
37where
38 F: FnOnce() -> R,
39{
40 crate::sync::critical_section::with_critical_section(object, f)
41}
42
43#[deprecated(
45 since = "0.28.0",
46 note = "use pyo3::sync::critical_section::with_critical_section2 instead"
47)]
48pub fn with_critical_section2<F, R>(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>, f: F) -> R
49where
50 F: FnOnce() -> R,
51{
52 crate::sync::critical_section::with_critical_section2(a, b, f)
53}
54pub use self::once_lock::PyOnceLock;
55
56#[deprecated(
81 since = "0.26.0",
82 note = "Prefer an interior mutability primitive compatible with free-threaded Python, such as `Mutex` in combination with the `MutexExt` trait"
83)]
84#[cfg(not(Py_GIL_DISABLED))]
85pub struct GILProtected<T> {
86 value: T,
87}
88
89#[allow(deprecated)]
90#[cfg(not(Py_GIL_DISABLED))]
91impl<T> GILProtected<T> {
92 pub const fn new(value: T) -> Self {
94 Self { value }
95 }
96
97 pub fn get<'py>(&'py self, _py: Python<'py>) -> &'py T {
99 &self.value
100 }
101
102 pub fn traverse<'py>(&'py self, _visit: PyVisit<'py>) -> &'py T {
104 &self.value
105 }
106}
107
108#[allow(deprecated)]
109#[cfg(not(Py_GIL_DISABLED))]
110unsafe impl<T> Sync for GILProtected<T> where T: Send {}
111
112#[doc = concat!(
122 "[the FAQ section](https://pyo3.rs/v",
123 env!("CARGO_PKG_VERSION"),
124 "/faq.html#im-experiencing-deadlocks-using-pyo3-with-stdsynconcelock-stdsynclazylock-lazy_static-and-once_cell)"
125)]
126#[deprecated(
164 since = "0.26.0",
165 note = "Prefer `pyo3::sync::PyOnceLock`, which avoids the possibility of racing during initialization."
166)]
167pub struct GILOnceCell<T> {
168 once: Once,
169 data: UnsafeCell<MaybeUninit<T>>,
170
171 _marker: PhantomData<T>,
193}
194
195#[allow(deprecated)]
196impl<T> Default for GILOnceCell<T> {
197 fn default() -> Self {
198 Self::new()
199 }
200}
201
202#[allow(deprecated)]
206unsafe impl<T: Send + Sync> Sync for GILOnceCell<T> {}
207#[allow(deprecated)]
208unsafe impl<T: Send> Send for GILOnceCell<T> {}
209
210#[allow(deprecated)]
211impl<T> GILOnceCell<T> {
212 pub const fn new() -> Self {
214 Self {
215 once: Once::new(),
216 data: UnsafeCell::new(MaybeUninit::uninit()),
217 _marker: PhantomData,
218 }
219 }
220
221 #[inline]
223 pub fn get(&self, _py: Python<'_>) -> Option<&T> {
224 if self.once.is_completed() {
225 Some(unsafe { (*self.data.get()).assume_init_ref() })
227 } else {
228 None
229 }
230 }
231
232 #[inline]
237 pub fn get_or_init<F>(&self, py: Python<'_>, f: F) -> &T
238 where
239 F: FnOnce() -> T,
240 {
241 if let Some(value) = self.get(py) {
242 return value;
243 }
244
245 self.init(py, || Ok::<T, std::convert::Infallible>(f()))
247 .unwrap()
248 }
249
250 #[inline]
255 pub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
256 where
257 F: FnOnce() -> Result<T, E>,
258 {
259 if let Some(value) = self.get(py) {
260 return Ok(value);
261 }
262
263 self.init(py, f)
264 }
265
266 #[cold]
267 fn init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
268 where
269 F: FnOnce() -> Result<T, E>,
270 {
271 let value = f()?;
279 let _ = self.set(py, value);
280
281 Ok(self.get(py).unwrap())
282 }
283
284 pub fn get_mut(&mut self) -> Option<&mut T> {
287 if self.once.is_completed() {
288 Some(unsafe { (*self.data.get()).assume_init_mut() })
290 } else {
291 None
292 }
293 }
294
295 pub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T> {
300 let mut value = Some(value);
301 self.once.call_once_force(|_| {
305 unsafe {
308 (*self.data.get()).write(value.take().unwrap());
310 }
311 });
312
313 match value {
314 Some(value) => Err(value),
316 None => Ok(()),
317 }
318 }
319
320 pub fn take(&mut self) -> Option<T> {
324 if self.once.is_completed() {
325 self.once = Once::new();
328 Some(unsafe { self.data.get_mut().assume_init_read() })
331 } else {
332 None
333 }
334 }
335
336 pub fn into_inner(mut self) -> Option<T> {
340 self.take()
341 }
342}
343
344#[allow(deprecated)]
345impl<T> GILOnceCell<Py<T>> {
346 pub fn clone_ref(&self, py: Python<'_>) -> Self {
350 let cloned = Self {
351 once: Once::new(),
352 data: UnsafeCell::new(MaybeUninit::uninit()),
353 _marker: PhantomData,
354 };
355 if let Some(value) = self.get(py) {
356 let _ = cloned.set(py, value.clone_ref(py));
357 }
358 cloned
359 }
360}
361
362#[allow(deprecated)]
363impl<T> GILOnceCell<Py<T>>
364where
365 T: PyTypeCheck,
366{
367 pub fn import<'py>(
400 &self,
401 py: Python<'py>,
402 module_name: &str,
403 attr_name: &str,
404 ) -> PyResult<&Bound<'py, T>> {
405 self.get_or_try_init(py, || {
406 let type_object = py.import(module_name)?.getattr(attr_name)?.cast_into()?;
407 Ok(type_object.unbind())
408 })
409 .map(|ty| ty.bind(py))
410 }
411}
412
413#[allow(deprecated)]
414impl<T> Drop for GILOnceCell<T> {
415 fn drop(&mut self) {
416 if self.once.is_completed() {
417 unsafe { MaybeUninit::assume_init_drop(self.data.get_mut()) }
419 }
420 }
421}
422
423#[macro_export]
461macro_rules! intern {
462 ($py: expr, $text: expr) => {{
463 static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text);
464 INTERNED.get($py)
465 }};
466}
467
468#[doc(hidden)]
470pub struct Interned(&'static str, PyOnceLock<Py<PyString>>);
471
472impl Interned {
473 pub const fn new(value: &'static str) -> Self {
475 Interned(value, PyOnceLock::new())
476 }
477
478 #[inline]
480 pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> {
481 self.1
482 .get_or_init(py, || PyString::intern(py, self.0).into())
483 .bind(py)
484 }
485}
486
487pub trait OnceExt: Sealed {
490 type OnceState;
492
493 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce());
496
497 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&Self::OnceState));
500}
501
502pub trait OnceLockExt<T>: once_lock_ext_sealed::Sealed {
505 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
515 where
516 F: FnOnce() -> T;
517}
518
519pub trait MutexExt<T>: Sealed {
522 type LockResult<'a>
524 where
525 Self: 'a;
526
527 fn lock_py_attached(&self, py: Python<'_>) -> Self::LockResult<'_>;
535}
536
537pub trait RwLockExt<T>: rwlock_ext_sealed::Sealed {
540 type ReadLockResult<'a>
542 where
543 Self: 'a;
544
545 type WriteLockResult<'a>
547 where
548 Self: 'a;
549
550 fn read_py_attached(&self, py: Python<'_>) -> Self::ReadLockResult<'_>;
559
560 fn write_py_attached(&self, py: Python<'_>) -> Self::WriteLockResult<'_>;
569}
570
571impl OnceExt for Once {
572 type OnceState = OnceState;
573
574 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce()) {
575 if self.is_completed() {
576 return;
577 }
578
579 init_once_py_attached(self, py, f)
580 }
581
582 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&OnceState)) {
583 if self.is_completed() {
584 return;
585 }
586
587 init_once_force_py_attached(self, py, f);
588 }
589}
590
591#[cfg(feature = "parking_lot")]
592impl OnceExt for parking_lot::Once {
593 type OnceState = parking_lot::OnceState;
594
595 fn call_once_py_attached(&self, _py: Python<'_>, f: impl FnOnce()) {
596 if self.state().done() {
597 return;
598 }
599
600 let ts_guard = unsafe { SuspendAttach::new() };
601
602 self.call_once(move || {
603 drop(ts_guard);
604 f();
605 });
606 }
607
608 fn call_once_force_py_attached(
609 &self,
610 _py: Python<'_>,
611 f: impl FnOnce(&parking_lot::OnceState),
612 ) {
613 if self.state().done() {
614 return;
615 }
616
617 let ts_guard = unsafe { SuspendAttach::new() };
618
619 self.call_once_force(move |state| {
620 drop(ts_guard);
621 f(&state);
622 });
623 }
624}
625
626impl<T> OnceLockExt<T> for std::sync::OnceLock<T> {
627 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
628 where
629 F: FnOnce() -> T,
630 {
631 self.get()
633 .unwrap_or_else(|| init_once_lock_py_attached(self, py, f))
634 }
635}
636
637impl<T> MutexExt<T> for std::sync::Mutex<T> {
638 type LockResult<'a>
639 = std::sync::LockResult<std::sync::MutexGuard<'a, T>>
640 where
641 Self: 'a;
642
643 fn lock_py_attached(
644 &self,
645 _py: Python<'_>,
646 ) -> std::sync::LockResult<std::sync::MutexGuard<'_, T>> {
647 match self.try_lock() {
652 Ok(inner) => return Ok(inner),
653 Err(std::sync::TryLockError::Poisoned(inner)) => {
654 return std::sync::LockResult::Err(inner)
655 }
656 Err(std::sync::TryLockError::WouldBlock) => {}
657 }
658 let ts_guard = unsafe { SuspendAttach::new() };
662 let res = self.lock();
663 drop(ts_guard);
664 res
665 }
666}
667
668#[cfg(feature = "lock_api")]
669impl<R: lock_api::RawMutex, T> MutexExt<T> for lock_api::Mutex<R, T> {
670 type LockResult<'a>
671 = lock_api::MutexGuard<'a, R, T>
672 where
673 Self: 'a;
674
675 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::MutexGuard<'_, R, T> {
676 if let Some(guard) = self.try_lock() {
677 return guard;
678 }
679
680 let ts_guard = unsafe { SuspendAttach::new() };
681 let res = self.lock();
682 drop(ts_guard);
683 res
684 }
685}
686
687#[cfg(feature = "arc_lock")]
688impl<R, T> MutexExt<T> for std::sync::Arc<lock_api::Mutex<R, T>>
689where
690 R: lock_api::RawMutex,
691{
692 type LockResult<'a>
693 = lock_api::ArcMutexGuard<R, T>
694 where
695 Self: 'a;
696
697 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcMutexGuard<R, T> {
698 if let Some(guard) = self.try_lock_arc() {
699 return guard;
700 }
701
702 let ts_guard = unsafe { SuspendAttach::new() };
703 let res = self.lock_arc();
704 drop(ts_guard);
705 res
706 }
707}
708
709#[cfg(feature = "lock_api")]
710impl<R, G, T> MutexExt<T> for lock_api::ReentrantMutex<R, G, T>
711where
712 R: lock_api::RawMutex,
713 G: lock_api::GetThreadId,
714{
715 type LockResult<'a>
716 = lock_api::ReentrantMutexGuard<'a, R, G, T>
717 where
718 Self: 'a;
719
720 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ReentrantMutexGuard<'_, R, G, T> {
721 if let Some(guard) = self.try_lock() {
722 return guard;
723 }
724
725 let ts_guard = unsafe { SuspendAttach::new() };
726 let res = self.lock();
727 drop(ts_guard);
728 res
729 }
730}
731
732#[cfg(feature = "arc_lock")]
733impl<R, G, T> MutexExt<T> for std::sync::Arc<lock_api::ReentrantMutex<R, G, T>>
734where
735 R: lock_api::RawMutex,
736 G: lock_api::GetThreadId,
737{
738 type LockResult<'a>
739 = lock_api::ArcReentrantMutexGuard<R, G, T>
740 where
741 Self: 'a;
742
743 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcReentrantMutexGuard<R, G, T> {
744 if let Some(guard) = self.try_lock_arc() {
745 return guard;
746 }
747
748 let ts_guard = unsafe { SuspendAttach::new() };
749 let res = self.lock_arc();
750 drop(ts_guard);
751 res
752 }
753}
754
755impl<T> RwLockExt<T> for std::sync::RwLock<T> {
756 type ReadLockResult<'a>
757 = std::sync::LockResult<std::sync::RwLockReadGuard<'a, T>>
758 where
759 Self: 'a;
760
761 type WriteLockResult<'a>
762 = std::sync::LockResult<std::sync::RwLockWriteGuard<'a, T>>
763 where
764 Self: 'a;
765
766 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
767 match self.try_read() {
772 Ok(inner) => return Ok(inner),
773 Err(std::sync::TryLockError::Poisoned(inner)) => {
774 return std::sync::LockResult::Err(inner)
775 }
776 Err(std::sync::TryLockError::WouldBlock) => {}
777 }
778
779 let ts_guard = unsafe { SuspendAttach::new() };
783
784 let res = self.read();
785 drop(ts_guard);
786 res
787 }
788
789 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
790 match self.try_write() {
795 Ok(inner) => return Ok(inner),
796 Err(std::sync::TryLockError::Poisoned(inner)) => {
797 return std::sync::LockResult::Err(inner)
798 }
799 Err(std::sync::TryLockError::WouldBlock) => {}
800 }
801
802 let ts_guard = unsafe { SuspendAttach::new() };
806
807 let res = self.write();
808 drop(ts_guard);
809 res
810 }
811}
812
813#[cfg(feature = "lock_api")]
814impl<R: lock_api::RawRwLock, T> RwLockExt<T> for lock_api::RwLock<R, T> {
815 type ReadLockResult<'a>
816 = lock_api::RwLockReadGuard<'a, R, T>
817 where
818 Self: 'a;
819
820 type WriteLockResult<'a>
821 = lock_api::RwLockWriteGuard<'a, R, T>
822 where
823 Self: 'a;
824
825 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
826 if let Some(guard) = self.try_read() {
827 return guard;
828 }
829
830 let ts_guard = unsafe { SuspendAttach::new() };
831 let res = self.read();
832 drop(ts_guard);
833 res
834 }
835
836 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
837 if let Some(guard) = self.try_write() {
838 return guard;
839 }
840
841 let ts_guard = unsafe { SuspendAttach::new() };
842 let res = self.write();
843 drop(ts_guard);
844 res
845 }
846}
847
848#[cfg(feature = "arc_lock")]
849impl<R, T> RwLockExt<T> for std::sync::Arc<lock_api::RwLock<R, T>>
850where
851 R: lock_api::RawRwLock,
852{
853 type ReadLockResult<'a>
854 = lock_api::ArcRwLockReadGuard<R, T>
855 where
856 Self: 'a;
857
858 type WriteLockResult<'a>
859 = lock_api::ArcRwLockWriteGuard<R, T>
860 where
861 Self: 'a;
862
863 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
864 if let Some(guard) = self.try_read_arc() {
865 return guard;
866 }
867
868 let ts_guard = unsafe { SuspendAttach::new() };
869 let res = self.read_arc();
870 drop(ts_guard);
871 res
872 }
873
874 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
875 if let Some(guard) = self.try_write_arc() {
876 return guard;
877 }
878
879 let ts_guard = unsafe { SuspendAttach::new() };
880 let res = self.write_arc();
881 drop(ts_guard);
882 res
883 }
884}
885
886#[cold]
887fn init_once_py_attached<F, T>(once: &Once, _py: Python<'_>, f: F)
888where
889 F: FnOnce() -> T,
890{
891 let ts_guard = unsafe { SuspendAttach::new() };
895
896 once.call_once(move || {
897 drop(ts_guard);
898 f();
899 });
900}
901
902#[cold]
903fn init_once_force_py_attached<F, T>(once: &Once, _py: Python<'_>, f: F)
904where
905 F: FnOnce(&OnceState) -> T,
906{
907 let ts_guard = unsafe { SuspendAttach::new() };
911
912 once.call_once_force(move |state| {
913 drop(ts_guard);
914 f(state);
915 });
916}
917
918#[cold]
919fn init_once_lock_py_attached<'a, F, T>(
920 lock: &'a std::sync::OnceLock<T>,
921 _py: Python<'_>,
922 f: F,
923) -> &'a T
924where
925 F: FnOnce() -> T,
926{
927 let ts_guard = unsafe { SuspendAttach::new() };
931
932 let value = lock.get_or_init(move || {
935 drop(ts_guard);
936 f()
937 });
938
939 value
940}
941
942mod once_lock_ext_sealed {
943 pub trait Sealed {}
944 impl<T> Sealed for std::sync::OnceLock<T> {}
945}
946
947mod rwlock_ext_sealed {
948 pub trait Sealed {}
949 impl<T> Sealed for std::sync::RwLock<T> {}
950 #[cfg(feature = "lock_api")]
951 impl<R, T> Sealed for lock_api::RwLock<R, T> {}
952 #[cfg(feature = "arc_lock")]
953 impl<R, T> Sealed for std::sync::Arc<lock_api::RwLock<R, T>> {}
954}
955
956#[cfg(test)]
957mod tests {
958 use super::*;
959
960 use crate::types::{PyDict, PyDictMethods};
961 #[cfg(not(target_arch = "wasm32"))]
962 #[cfg(feature = "macros")]
963 use std::sync::atomic::{AtomicBool, Ordering};
964 #[cfg(not(target_arch = "wasm32"))]
965 #[cfg(feature = "macros")]
966 use std::sync::Barrier;
967 #[cfg(not(target_arch = "wasm32"))]
968 use std::sync::Mutex;
969
970 #[cfg(not(target_arch = "wasm32"))]
971 #[cfg(feature = "macros")]
972 #[crate::pyclass(crate = "crate")]
973 struct BoolWrapper(AtomicBool);
974
975 #[test]
976 fn test_intern() {
977 Python::attach(|py| {
978 let foo1 = "foo";
979 let foo2 = intern!(py, "foo");
980 let foo3 = intern!(py, stringify!(foo));
981
982 let dict = PyDict::new(py);
983 dict.set_item(foo1, 42_usize).unwrap();
984 assert!(dict.contains(foo2).unwrap());
985 assert_eq!(
986 dict.get_item(foo3)
987 .unwrap()
988 .unwrap()
989 .extract::<usize>()
990 .unwrap(),
991 42
992 );
993 });
994 }
995
996 #[test]
997 #[allow(deprecated)]
998 fn test_once_cell() {
999 Python::attach(|py| {
1000 let mut cell = GILOnceCell::new();
1001
1002 assert!(cell.get(py).is_none());
1003
1004 assert_eq!(cell.get_or_try_init(py, || Err(5)), Err(5));
1005 assert!(cell.get(py).is_none());
1006
1007 assert_eq!(cell.get_or_try_init(py, || Ok::<_, ()>(2)), Ok(&2));
1008 assert_eq!(cell.get(py), Some(&2));
1009
1010 assert_eq!(cell.get_or_try_init(py, || Err(5)), Ok(&2));
1011
1012 assert_eq!(cell.take(), Some(2));
1013 assert_eq!(cell.into_inner(), None);
1014
1015 let cell_py = GILOnceCell::new();
1016 assert!(cell_py.clone_ref(py).get(py).is_none());
1017 cell_py.get_or_init(py, || py.None());
1018 assert!(cell_py.clone_ref(py).get(py).unwrap().is_none(py));
1019 })
1020 }
1021
1022 #[test]
1023 #[allow(deprecated)]
1024 fn test_once_cell_drop() {
1025 #[derive(Debug)]
1026 struct RecordDrop<'a>(&'a mut bool);
1027
1028 impl Drop for RecordDrop<'_> {
1029 fn drop(&mut self) {
1030 *self.0 = true;
1031 }
1032 }
1033
1034 Python::attach(|py| {
1035 let mut dropped = false;
1036 let cell = GILOnceCell::new();
1037 cell.set(py, RecordDrop(&mut dropped)).unwrap();
1038 let drop_container = cell.get(py).unwrap();
1039
1040 assert!(!*drop_container.0);
1041 drop(cell);
1042 assert!(dropped);
1043 });
1044 }
1045
1046 #[test]
1047 #[cfg(not(target_arch = "wasm32"))] fn test_once_ext() {
1049 macro_rules! test_once {
1050 ($once:expr, $is_poisoned:expr) => {{
1051 let init = $once;
1053 std::thread::scope(|s| {
1054 let handle = s.spawn(|| {
1056 Python::attach(|py| {
1057 init.call_once_py_attached(py, || panic!());
1058 })
1059 });
1060 assert!(handle.join().is_err());
1061
1062 let handle = s.spawn(|| {
1064 Python::attach(|py| {
1065 init.call_once_py_attached(py, || {});
1066 });
1067 });
1068
1069 assert!(handle.join().is_err());
1070
1071 Python::attach(|py| {
1073 init.call_once_force_py_attached(py, |state| {
1074 assert!($is_poisoned(state.clone()));
1075 });
1076
1077 init.call_once_py_attached(py, || {});
1079 });
1080
1081 Python::attach(|py| init.call_once_force_py_attached(py, |_| panic!()));
1083 });
1084 }};
1085 }
1086
1087 test_once!(Once::new(), OnceState::is_poisoned);
1088 #[cfg(feature = "parking_lot")]
1089 test_once!(parking_lot::Once::new(), parking_lot::OnceState::poisoned);
1090 }
1091
1092 #[cfg(not(target_arch = "wasm32"))] #[test]
1094 fn test_once_lock_ext() {
1095 let cell = std::sync::OnceLock::new();
1096 std::thread::scope(|s| {
1097 assert!(cell.get().is_none());
1098
1099 s.spawn(|| {
1100 Python::attach(|py| {
1101 assert_eq!(*cell.get_or_init_py_attached(py, || 12345), 12345);
1102 });
1103 });
1104 });
1105 assert_eq!(cell.get(), Some(&12345));
1106 }
1107
1108 #[cfg(feature = "macros")]
1109 #[cfg(not(target_arch = "wasm32"))] #[test]
1111 fn test_mutex_ext() {
1112 let barrier = Barrier::new(2);
1113
1114 let mutex = Python::attach(|py| -> Mutex<Py<BoolWrapper>> {
1115 Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1116 });
1117
1118 std::thread::scope(|s| {
1119 s.spawn(|| {
1120 Python::attach(|py| {
1121 let b = mutex.lock_py_attached(py).unwrap();
1122 barrier.wait();
1123 std::thread::sleep(std::time::Duration::from_millis(10));
1125 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1126 drop(b);
1127 });
1128 });
1129 s.spawn(|| {
1130 barrier.wait();
1131 Python::attach(|py| {
1132 let b = mutex.lock_py_attached(py).unwrap();
1134 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1135 });
1136 });
1137 });
1138 }
1139
1140 #[cfg(feature = "macros")]
1141 #[cfg(all(
1142 any(feature = "parking_lot", feature = "lock_api"),
1143 not(target_arch = "wasm32") ))]
1145 #[test]
1146 fn test_parking_lot_mutex_ext() {
1147 macro_rules! test_mutex {
1148 ($guard:ty ,$mutex:stmt) => {{
1149 let barrier = Barrier::new(2);
1150
1151 let mutex = Python::attach({ $mutex });
1152
1153 std::thread::scope(|s| {
1154 s.spawn(|| {
1155 Python::attach(|py| {
1156 let b: $guard = mutex.lock_py_attached(py);
1157 barrier.wait();
1158 std::thread::sleep(std::time::Duration::from_millis(10));
1160 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1161 drop(b);
1162 });
1163 });
1164 s.spawn(|| {
1165 barrier.wait();
1166 Python::attach(|py| {
1167 let b: $guard = mutex.lock_py_attached(py);
1169 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1170 });
1171 });
1172 });
1173 }};
1174 }
1175
1176 test_mutex!(parking_lot::MutexGuard<'_, _>, |py| {
1177 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1178 });
1179
1180 test_mutex!(parking_lot::ReentrantMutexGuard<'_, _>, |py| {
1181 parking_lot::ReentrantMutex::new(
1182 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1183 )
1184 });
1185
1186 #[cfg(feature = "arc_lock")]
1187 test_mutex!(parking_lot::ArcMutexGuard<_, _>, |py| {
1188 let mutex =
1189 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
1190 std::sync::Arc::new(mutex)
1191 });
1192
1193 #[cfg(feature = "arc_lock")]
1194 test_mutex!(parking_lot::ArcReentrantMutexGuard<_, _, _>, |py| {
1195 let mutex =
1196 parking_lot::ReentrantMutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
1197 std::sync::Arc::new(mutex)
1198 });
1199 }
1200
1201 #[cfg(not(target_arch = "wasm32"))] #[test]
1203 fn test_mutex_ext_poison() {
1204 let mutex = Mutex::new(42);
1205
1206 std::thread::scope(|s| {
1207 let lock_result = s.spawn(|| {
1208 Python::attach(|py| {
1209 let _unused = mutex.lock_py_attached(py);
1210 panic!();
1211 });
1212 });
1213 assert!(lock_result.join().is_err());
1214 assert!(mutex.is_poisoned());
1215 });
1216 let guard = Python::attach(|py| {
1217 match mutex.lock_py_attached(py) {
1219 Ok(guard) => guard,
1220 Err(poisoned) => poisoned.into_inner(),
1221 }
1222 });
1223 assert_eq!(*guard, 42);
1224 }
1225
1226 #[cfg(feature = "macros")]
1227 #[cfg(not(target_arch = "wasm32"))] #[test]
1229 fn test_rwlock_ext_writer_blocks_reader() {
1230 use std::sync::RwLock;
1231
1232 let barrier = Barrier::new(2);
1233
1234 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
1235 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1236 });
1237
1238 std::thread::scope(|s| {
1239 s.spawn(|| {
1240 Python::attach(|py| {
1241 let b = rwlock.write_py_attached(py).unwrap();
1242 barrier.wait();
1243 std::thread::sleep(std::time::Duration::from_millis(10));
1245 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1246 drop(b);
1247 });
1248 });
1249 s.spawn(|| {
1250 barrier.wait();
1251 Python::attach(|py| {
1252 let b = rwlock.read_py_attached(py).unwrap();
1254 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1255 });
1256 });
1257 });
1258 }
1259
1260 #[cfg(feature = "macros")]
1261 #[cfg(not(target_arch = "wasm32"))] #[test]
1263 fn test_rwlock_ext_reader_blocks_writer() {
1264 use std::sync::RwLock;
1265
1266 let barrier = Barrier::new(2);
1267
1268 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
1269 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1270 });
1271
1272 std::thread::scope(|s| {
1273 s.spawn(|| {
1274 Python::attach(|py| {
1275 let b = rwlock.read_py_attached(py).unwrap();
1276 barrier.wait();
1277
1278 std::thread::sleep(std::time::Duration::from_millis(10));
1280
1281 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire));
1284 });
1285 });
1286 s.spawn(|| {
1287 barrier.wait();
1288 Python::attach(|py| {
1289 let b = rwlock.write_py_attached(py).unwrap();
1291 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1292 drop(b);
1293 });
1294 });
1295 });
1296
1297 Python::attach(|py| {
1299 let b = rwlock.read_py_attached(py).unwrap();
1300 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1301 drop(b);
1302 });
1303 }
1304
1305 #[cfg(feature = "macros")]
1306 #[cfg(all(
1307 any(feature = "parking_lot", feature = "lock_api"),
1308 not(target_arch = "wasm32") ))]
1310 #[test]
1311 fn test_parking_lot_rwlock_ext_writer_blocks_reader() {
1312 macro_rules! test_rwlock {
1313 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1314 let barrier = Barrier::new(2);
1315
1316 let rwlock = Python::attach({ $rwlock });
1317
1318 std::thread::scope(|s| {
1319 s.spawn(|| {
1320 Python::attach(|py| {
1321 let b: $write_guard = rwlock.write_py_attached(py);
1322 barrier.wait();
1323 std::thread::sleep(std::time::Duration::from_millis(10));
1325 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1326 drop(b);
1327 });
1328 });
1329 s.spawn(|| {
1330 barrier.wait();
1331 Python::attach(|py| {
1332 let b: $read_guard = rwlock.read_py_attached(py);
1334 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1335 });
1336 });
1337 });
1338 }};
1339 }
1340
1341 test_rwlock!(
1342 parking_lot::RwLockWriteGuard<'_, _>,
1343 parking_lot::RwLockReadGuard<'_, _>,
1344 |py| {
1345 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1346 }
1347 );
1348
1349 #[cfg(feature = "arc_lock")]
1350 test_rwlock!(
1351 parking_lot::ArcRwLockWriteGuard<_, _>,
1352 parking_lot::ArcRwLockReadGuard<_, _>,
1353 |py| {
1354 let rwlock = parking_lot::RwLock::new(
1355 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1356 );
1357 std::sync::Arc::new(rwlock)
1358 }
1359 );
1360 }
1361
1362 #[cfg(feature = "macros")]
1363 #[cfg(all(
1364 any(feature = "parking_lot", feature = "lock_api"),
1365 not(target_arch = "wasm32") ))]
1367 #[test]
1368 fn test_parking_lot_rwlock_ext_reader_blocks_writer() {
1369 macro_rules! test_rwlock {
1370 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1371 let barrier = Barrier::new(2);
1372
1373 let rwlock = Python::attach({ $rwlock });
1374
1375 std::thread::scope(|s| {
1376 s.spawn(|| {
1377 Python::attach(|py| {
1378 let b: $read_guard = rwlock.read_py_attached(py);
1379 barrier.wait();
1380
1381 std::thread::sleep(std::time::Duration::from_millis(10));
1383
1384 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire)); (*b).bind(py).borrow().0.store(true, Ordering::Release);
1387
1388 drop(b);
1389 });
1390 });
1391 s.spawn(|| {
1392 barrier.wait();
1393 Python::attach(|py| {
1394 let b: $write_guard = rwlock.write_py_attached(py);
1396 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1397 });
1398 });
1399 });
1400
1401 Python::attach(|py| {
1403 let b: $read_guard = rwlock.read_py_attached(py);
1404 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1405 drop(b);
1406 });
1407 }};
1408 }
1409
1410 test_rwlock!(
1411 parking_lot::RwLockWriteGuard<'_, _>,
1412 parking_lot::RwLockReadGuard<'_, _>,
1413 |py| {
1414 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1415 }
1416 );
1417
1418 #[cfg(feature = "arc_lock")]
1419 test_rwlock!(
1420 parking_lot::ArcRwLockWriteGuard<_, _>,
1421 parking_lot::ArcRwLockReadGuard<_, _>,
1422 |py| {
1423 let rwlock = parking_lot::RwLock::new(
1424 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1425 );
1426 std::sync::Arc::new(rwlock)
1427 }
1428 );
1429 }
1430
1431 #[cfg(not(target_arch = "wasm32"))] #[test]
1433 fn test_rwlock_ext_poison() {
1434 use std::sync::RwLock;
1435
1436 let rwlock = RwLock::new(42);
1437
1438 std::thread::scope(|s| {
1439 let lock_result = s.spawn(|| {
1440 Python::attach(|py| {
1441 let _unused = rwlock.write_py_attached(py);
1442 panic!();
1443 });
1444 });
1445 assert!(lock_result.join().is_err());
1446 assert!(rwlock.is_poisoned());
1447 Python::attach(|py| {
1448 assert!(rwlock.read_py_attached(py).is_err());
1449 assert!(rwlock.write_py_attached(py).is_err());
1450 });
1451 });
1452 Python::attach(|py| {
1453 let guard = rwlock.write_py_attached(py).unwrap_err().into_inner();
1455 assert_eq!(*guard, 42);
1456 });
1457 }
1458}