1use crate::platform::sync::Once;
13use crate::{
14 internal::state::SuspendAttach,
15 sealed::Sealed,
16 types::{PyAny, PyString},
17 Bound, Py, Python,
18};
19use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit};
20
21pub mod critical_section;
22#[cfg(all(not(Py_LIMITED_API), Py_3_13))]
23mod mutex;
24pub(crate) mod once_lock;
25
26#[cfg(all(not(Py_LIMITED_API), Py_3_13))]
27pub use self::mutex::{PyMutex, PyMutexGuard};
28
29#[deprecated(
31 since = "0.28.0",
32 note = "use pyo3::sync::critical_section::with_critical_section instead"
33)]
34pub fn with_critical_section<F, R>(object: &Bound<'_, PyAny>, f: F) -> R
35where
36 F: FnOnce() -> R,
37{
38 crate::sync::critical_section::with_critical_section(object, f)
39}
40
41#[deprecated(
43 since = "0.28.0",
44 note = "use pyo3::sync::critical_section::with_critical_section2 instead"
45)]
46pub fn with_critical_section2<F, R>(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>, f: F) -> R
47where
48 F: FnOnce() -> R,
49{
50 crate::sync::critical_section::with_critical_section2(a, b, f)
51}
52pub use self::once_lock::PyOnceLock;
53
54#[deprecated(
55 since = "0.26.0",
56 note = "Now internal only, to be removed after https://github.com/PyO3/pyo3/pull/5341"
57)]
58pub(crate) struct GILOnceCell<T> {
59 once: Once,
60 data: UnsafeCell<MaybeUninit<T>>,
61
62 _marker: PhantomData<T>,
84}
85
86#[allow(deprecated)]
87impl<T> Default for GILOnceCell<T> {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93#[allow(deprecated)]
98unsafe impl<T: Send + Sync> Sync for GILOnceCell<T> {}
99#[allow(deprecated)]
101unsafe impl<T: Send> Send for GILOnceCell<T> {}
102
103#[allow(deprecated)]
104impl<T> GILOnceCell<T> {
105 pub const fn new() -> Self {
107 Self {
108 once: Once::new(),
109 data: UnsafeCell::new(MaybeUninit::uninit()),
110 _marker: PhantomData,
111 }
112 }
113
114 #[inline]
116 pub fn get(&self, _py: Python<'_>) -> Option<&T> {
117 if self.once.is_completed() {
118 Some(unsafe { (*self.data.get()).assume_init_ref() })
120 } else {
121 None
122 }
123 }
124
125 #[inline]
130 pub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
131 where
132 F: FnOnce() -> Result<T, E>,
133 {
134 if let Some(value) = self.get(py) {
135 return Ok(value);
136 }
137
138 self.init(py, f)
139 }
140
141 #[cold]
142 fn init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
143 where
144 F: FnOnce() -> Result<T, E>,
145 {
146 let value = f()?;
154 let _ = self.set(py, value);
155
156 Ok(self.get(py).unwrap())
157 }
158
159 pub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T> {
164 let mut value = Some(value);
165 self.once.call_once_force(|| {
169 unsafe {
172 (*self.data.get()).write(value.take().unwrap());
174 }
175 });
176
177 match value {
178 Some(value) => Err(value),
180 None => Ok(()),
181 }
182 }
183}
184
185#[allow(deprecated)]
186impl<T> Drop for GILOnceCell<T> {
187 fn drop(&mut self) {
188 if self.once.is_completed() {
189 unsafe { MaybeUninit::assume_init_drop(self.data.get_mut()) }
191 }
192 }
193}
194
195#[macro_export]
233macro_rules! intern {
234 ($py: expr, $text: expr) => {{
235 static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text);
236 INTERNED.get($py)
237 }};
238}
239
240#[doc(hidden)]
242pub struct Interned(&'static str, PyOnceLock<Py<PyString>>);
243
244impl Interned {
245 pub const fn new(value: &'static str) -> Self {
247 Interned(value, PyOnceLock::new())
248 }
249
250 #[inline]
252 pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> {
253 self.1
254 .get_or_init(py, || PyString::intern(py, self.0).into())
255 .bind(py)
256 }
257}
258
259pub trait OnceExt: Sealed {
262 type OnceState;
264
265 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce());
268
269 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&Self::OnceState));
272}
273
274pub trait OnceLockExt<T>: once_lock_ext_sealed::Sealed {
277 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
287 where
288 F: FnOnce() -> T;
289}
290
291pub trait MutexExt<T>: Sealed {
294 type LockResult<'a>
296 where
297 Self: 'a;
298
299 fn lock_py_attached(&self, py: Python<'_>) -> Self::LockResult<'_>;
307}
308
309pub trait RwLockExt<T>: rwlock_ext_sealed::Sealed {
312 type ReadLockResult<'a>
314 where
315 Self: 'a;
316
317 type WriteLockResult<'a>
319 where
320 Self: 'a;
321
322 fn read_py_attached(&self, py: Python<'_>) -> Self::ReadLockResult<'_>;
331
332 fn write_py_attached(&self, py: Python<'_>) -> Self::WriteLockResult<'_>;
341}
342
343#[cfg(wip_feature_std)]
344#[allow(clippy::disallowed_types)]
345impl OnceExt for std::sync::Once {
346 type OnceState = std::sync::OnceState;
347
348 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce()) {
349 if self.is_completed() {
350 return;
351 }
352
353 init_once_py_attached(self, py, f)
354 }
355
356 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&std::sync::OnceState)) {
357 if self.is_completed() {
358 return;
359 }
360
361 init_once_force_py_attached(self, py, f);
362 }
363}
364
365#[cfg(feature = "parking_lot")]
366impl OnceExt for parking_lot::Once {
367 type OnceState = parking_lot::OnceState;
368
369 fn call_once_py_attached(&self, _py: Python<'_>, f: impl FnOnce()) {
370 if self.state().done() {
371 return;
372 }
373
374 let ts_guard = unsafe { SuspendAttach::new() };
378
379 self.call_once(move || {
380 drop(ts_guard);
381 f();
382 });
383 }
384
385 fn call_once_force_py_attached(
386 &self,
387 _py: Python<'_>,
388 f: impl FnOnce(&parking_lot::OnceState),
389 ) {
390 if self.state().done() {
391 return;
392 }
393
394 let ts_guard = unsafe { SuspendAttach::new() };
398
399 self.call_once_force(move |state| {
400 drop(ts_guard);
401 f(&state);
402 });
403 }
404}
405
406impl<T> OnceLockExt<T> for std::sync::OnceLock<T> {
407 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
408 where
409 F: FnOnce() -> T,
410 {
411 self.get()
413 .unwrap_or_else(|| init_once_lock_py_attached(self, py, f))
414 }
415}
416
417#[cfg(wip_feature_std)]
418#[allow(clippy::disallowed_types)]
419impl<T> MutexExt<T> for std::sync::Mutex<T> {
420 type LockResult<'a>
421 = std::sync::LockResult<std::sync::MutexGuard<'a, T>>
422 where
423 Self: 'a;
424
425 fn lock_py_attached(
426 &self,
427 _py: Python<'_>,
428 ) -> std::sync::LockResult<std::sync::MutexGuard<'_, T>> {
429 match self.try_lock() {
434 Ok(inner) => return Ok(inner),
435 Err(std::sync::TryLockError::Poisoned(inner)) => {
436 return std::sync::LockResult::Err(inner)
437 }
438 Err(std::sync::TryLockError::WouldBlock) => {}
439 }
440 let ts_guard = unsafe { SuspendAttach::new() };
444 let res = self.lock();
445 drop(ts_guard);
446 res
447 }
448}
449
450#[cfg(feature = "lock_api")]
451impl<R: lock_api::RawMutex, T> MutexExt<T> for lock_api::Mutex<R, T> {
452 type LockResult<'a>
453 = lock_api::MutexGuard<'a, R, T>
454 where
455 Self: 'a;
456
457 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::MutexGuard<'_, R, T> {
458 if let Some(guard) = self.try_lock() {
459 return guard;
460 }
461
462 let ts_guard = unsafe { SuspendAttach::new() };
466 let res = self.lock();
467 drop(ts_guard);
468 res
469 }
470}
471
472#[cfg(feature = "arc_lock")]
473impl<R, T> MutexExt<T> for alloc::sync::Arc<lock_api::Mutex<R, T>>
474where
475 R: lock_api::RawMutex,
476{
477 type LockResult<'a>
478 = lock_api::ArcMutexGuard<R, T>
479 where
480 Self: 'a;
481
482 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcMutexGuard<R, T> {
483 if let Some(guard) = self.try_lock_arc() {
484 return guard;
485 }
486
487 let ts_guard = unsafe { SuspendAttach::new() };
491 let res = self.lock_arc();
492 drop(ts_guard);
493 res
494 }
495}
496
497#[cfg(feature = "lock_api")]
498impl<R, G, T> MutexExt<T> for lock_api::ReentrantMutex<R, G, T>
499where
500 R: lock_api::RawMutex,
501 G: lock_api::GetThreadId,
502{
503 type LockResult<'a>
504 = lock_api::ReentrantMutexGuard<'a, R, G, T>
505 where
506 Self: 'a;
507
508 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ReentrantMutexGuard<'_, R, G, T> {
509 if let Some(guard) = self.try_lock() {
510 return guard;
511 }
512
513 let ts_guard = unsafe { SuspendAttach::new() };
517 let res = self.lock();
518 drop(ts_guard);
519 res
520 }
521}
522
523#[cfg(feature = "arc_lock")]
524impl<R, G, T> MutexExt<T> for alloc::sync::Arc<lock_api::ReentrantMutex<R, G, T>>
525where
526 R: lock_api::RawMutex,
527 G: lock_api::GetThreadId,
528{
529 type LockResult<'a>
530 = lock_api::ArcReentrantMutexGuard<R, G, T>
531 where
532 Self: 'a;
533
534 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcReentrantMutexGuard<R, G, T> {
535 if let Some(guard) = self.try_lock_arc() {
536 return guard;
537 }
538
539 let ts_guard = unsafe { SuspendAttach::new() };
543 let res = self.lock_arc();
544 drop(ts_guard);
545 res
546 }
547}
548
549impl<T> RwLockExt<T> for std::sync::RwLock<T> {
550 type ReadLockResult<'a>
551 = std::sync::LockResult<std::sync::RwLockReadGuard<'a, T>>
552 where
553 Self: 'a;
554
555 type WriteLockResult<'a>
556 = std::sync::LockResult<std::sync::RwLockWriteGuard<'a, T>>
557 where
558 Self: 'a;
559
560 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
561 match self.try_read() {
566 Ok(inner) => return Ok(inner),
567 Err(std::sync::TryLockError::Poisoned(inner)) => {
568 return std::sync::LockResult::Err(inner)
569 }
570 Err(std::sync::TryLockError::WouldBlock) => {}
571 }
572
573 let ts_guard = unsafe { SuspendAttach::new() };
577
578 let res = self.read();
579 drop(ts_guard);
580 res
581 }
582
583 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
584 match self.try_write() {
589 Ok(inner) => return Ok(inner),
590 Err(std::sync::TryLockError::Poisoned(inner)) => {
591 return std::sync::LockResult::Err(inner)
592 }
593 Err(std::sync::TryLockError::WouldBlock) => {}
594 }
595
596 let ts_guard = unsafe { SuspendAttach::new() };
600
601 let res = self.write();
602 drop(ts_guard);
603 res
604 }
605}
606
607#[cfg(feature = "lock_api")]
608impl<R: lock_api::RawRwLock, T> RwLockExt<T> for lock_api::RwLock<R, T> {
609 type ReadLockResult<'a>
610 = lock_api::RwLockReadGuard<'a, R, T>
611 where
612 Self: 'a;
613
614 type WriteLockResult<'a>
615 = lock_api::RwLockWriteGuard<'a, R, T>
616 where
617 Self: 'a;
618
619 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
620 if let Some(guard) = self.try_read() {
621 return guard;
622 }
623
624 let ts_guard = unsafe { SuspendAttach::new() };
628 let res = self.read();
629 drop(ts_guard);
630 res
631 }
632
633 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
634 if let Some(guard) = self.try_write() {
635 return guard;
636 }
637
638 let ts_guard = unsafe { SuspendAttach::new() };
642 let res = self.write();
643 drop(ts_guard);
644 res
645 }
646}
647
648#[cfg(feature = "arc_lock")]
649impl<R, T> RwLockExt<T> for alloc::sync::Arc<lock_api::RwLock<R, T>>
650where
651 R: lock_api::RawRwLock,
652{
653 type ReadLockResult<'a>
654 = lock_api::ArcRwLockReadGuard<R, T>
655 where
656 Self: 'a;
657
658 type WriteLockResult<'a>
659 = lock_api::ArcRwLockWriteGuard<R, T>
660 where
661 Self: 'a;
662
663 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
664 if let Some(guard) = self.try_read_arc() {
665 return guard;
666 }
667
668 let ts_guard = unsafe { SuspendAttach::new() };
672 let res = self.read_arc();
673 drop(ts_guard);
674 res
675 }
676
677 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
678 if let Some(guard) = self.try_write_arc() {
679 return guard;
680 }
681
682 let ts_guard = unsafe { SuspendAttach::new() };
686 let res = self.write_arc();
687 drop(ts_guard);
688 res
689 }
690}
691
692#[cfg(wip_feature_std)]
693#[cold]
694#[allow(clippy::disallowed_types)]
695fn init_once_py_attached<F, T>(once: &std::sync::Once, _py: Python<'_>, f: F)
696where
697 F: FnOnce() -> T,
698{
699 let ts_guard = unsafe { SuspendAttach::new() };
703
704 once.call_once(move || {
705 drop(ts_guard);
706 f();
707 });
708}
709
710#[cfg(wip_feature_std)]
711#[cold]
712#[allow(clippy::disallowed_types)]
713fn init_once_force_py_attached<F, T>(once: &std::sync::Once, _py: Python<'_>, f: F)
714where
715 F: FnOnce(&std::sync::OnceState) -> T,
716{
717 let ts_guard = unsafe { SuspendAttach::new() };
721
722 once.call_once_force(move |state| {
723 drop(ts_guard);
724 f(state);
725 });
726}
727
728#[cold]
729fn init_once_lock_py_attached<'a, F, T>(
730 lock: &'a std::sync::OnceLock<T>,
731 _py: Python<'_>,
732 f: F,
733) -> &'a T
734where
735 F: FnOnce() -> T,
736{
737 let ts_guard = unsafe { SuspendAttach::new() };
741
742 let value = lock.get_or_init(move || {
745 drop(ts_guard);
746 f()
747 });
748
749 value
750}
751
752mod once_lock_ext_sealed {
753 pub trait Sealed {}
754 impl<T> Sealed for std::sync::OnceLock<T> {}
755}
756
757mod rwlock_ext_sealed {
758 pub trait Sealed {}
759 impl<T> Sealed for std::sync::RwLock<T> {}
760 #[cfg(feature = "lock_api")]
761 impl<R, T> Sealed for lock_api::RwLock<R, T> {}
762 #[cfg(feature = "arc_lock")]
763 impl<R, T> Sealed for alloc::sync::Arc<lock_api::RwLock<R, T>> {}
764}
765
766#[allow(clippy::disallowed_types, reason = "tests")]
767#[cfg(test)]
768mod tests {
769 use super::*;
770
771 use crate::types::{PyAnyMethods, PyDict, PyDictMethods};
772 #[cfg(not(target_arch = "wasm32"))]
773 #[cfg(feature = "macros")]
774 use core::sync::atomic::{AtomicBool, Ordering};
775 #[cfg(not(target_arch = "wasm32"))]
776 #[cfg(feature = "macros")]
777 use std::sync::Barrier;
778 #[cfg(wip_feature_std)]
779 #[cfg(not(target_arch = "wasm32"))]
780 use std::sync::Mutex;
781 #[cfg(wip_feature_std)]
782 #[cfg(not(target_arch = "wasm32"))]
783 use std::sync::{Once, OnceState};
784
785 #[cfg(not(target_arch = "wasm32"))]
786 #[cfg(feature = "macros")]
787 #[crate::pyclass(crate = "crate")]
788 struct BoolWrapper(AtomicBool);
789
790 #[test]
791 fn test_intern() {
792 Python::attach(|py| {
793 let foo1 = "foo";
794 let foo2 = intern!(py, "foo");
795 let foo3 = intern!(py, stringify!(foo));
796
797 let dict = PyDict::new(py);
798 dict.set_item(foo1, 42_usize).unwrap();
799 assert!(dict.contains(foo2).unwrap());
800 assert_eq!(
801 dict.get_item(foo3)
802 .unwrap()
803 .unwrap()
804 .extract::<usize>()
805 .unwrap(),
806 42
807 );
808 });
809 }
810
811 #[test]
812 #[allow(deprecated)]
813 fn test_once_cell() {
814 Python::attach(|py| {
815 let cell = GILOnceCell::new();
816
817 assert!(cell.get(py).is_none());
818
819 assert_eq!(cell.get_or_try_init(py, || Err(5)), Err(5));
820 assert!(cell.get(py).is_none());
821
822 assert_eq!(cell.get_or_try_init(py, || Ok::<_, ()>(2)), Ok(&2));
823 assert_eq!(cell.get(py), Some(&2));
824
825 assert_eq!(cell.get_or_try_init(py, || Err(5)), Ok(&2));
826 })
827 }
828
829 #[test]
830 #[allow(deprecated)]
831 fn test_once_cell_drop() {
832 #[derive(Debug)]
833 struct RecordDrop<'a>(&'a mut bool);
834
835 impl Drop for RecordDrop<'_> {
836 fn drop(&mut self) {
837 *self.0 = true;
838 }
839 }
840
841 Python::attach(|py| {
842 let mut dropped = false;
843 let cell = GILOnceCell::new();
844 cell.set(py, RecordDrop(&mut dropped)).unwrap();
845 let drop_container = cell.get(py).unwrap();
846
847 assert!(!*drop_container.0);
848 drop(cell);
849 assert!(dropped);
850 });
851 }
852
853 #[test]
854 #[cfg(not(target_arch = "wasm32"))] #[cfg(wip_feature_std)]
856 fn test_once_ext() {
857 macro_rules! test_once {
858 ($once:expr, $is_poisoned:expr) => {{
859 let init = $once;
861 std::thread::scope(|s| {
862 let handle = s.spawn(|| {
864 Python::attach(|py| {
865 init.call_once_py_attached(py, || panic!());
866 })
867 });
868 assert!(handle.join().is_err());
869
870 let handle = s.spawn(|| {
872 Python::attach(|py| {
873 init.call_once_py_attached(py, || {});
874 });
875 });
876
877 assert!(handle.join().is_err());
878
879 Python::attach(|py| {
881 init.call_once_force_py_attached(py, |state| {
882 assert!($is_poisoned(state.clone()));
883 });
884
885 init.call_once_py_attached(py, || {});
887 });
888
889 Python::attach(|py| init.call_once_force_py_attached(py, |_| panic!()));
891 });
892 }};
893 }
894
895 test_once!(Once::new(), OnceState::is_poisoned);
896 #[cfg(feature = "parking_lot")]
897 test_once!(parking_lot::Once::new(), parking_lot::OnceState::poisoned);
898 }
899
900 #[cfg(not(target_arch = "wasm32"))] #[cfg(wip_feature_std)]
902 #[test]
903 fn test_once_lock_ext() {
904 let cell = std::sync::OnceLock::new();
905 std::thread::scope(|s| {
906 assert!(cell.get().is_none());
907
908 s.spawn(|| {
909 Python::attach(|py| {
910 assert_eq!(*cell.get_or_init_py_attached(py, || 12345), 12345);
911 });
912 });
913 });
914 assert_eq!(cell.get(), Some(&12345));
915 }
916
917 #[cfg(feature = "macros")]
918 #[cfg(not(target_arch = "wasm32"))] #[cfg(wip_feature_std)]
920 #[test]
921 fn test_mutex_ext() {
922 let barrier = Barrier::new(2);
923
924 let mutex = Python::attach(|py| -> Mutex<Py<BoolWrapper>> {
925 Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
926 });
927
928 std::thread::scope(|s| {
929 s.spawn(|| {
930 Python::attach(|py| {
931 let b = mutex.lock_py_attached(py).unwrap();
932 barrier.wait();
933 std::thread::sleep(core::time::Duration::from_millis(10));
935 (*b).bind(py).borrow().0.store(true, Ordering::Release);
936 drop(b);
937 });
938 });
939 s.spawn(|| {
940 barrier.wait();
941 Python::attach(|py| {
942 let b = mutex.lock_py_attached(py).unwrap();
944 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
945 });
946 });
947 });
948 }
949
950 #[cfg(feature = "macros")]
951 #[cfg(all(
952 any(feature = "parking_lot", feature = "lock_api"),
953 not(target_arch = "wasm32") ))]
955 #[test]
956 fn test_parking_lot_mutex_ext() {
957 macro_rules! test_mutex {
958 ($guard:ty ,$mutex:stmt) => {{
959 let barrier = Barrier::new(2);
960
961 let mutex = Python::attach({ $mutex });
962
963 std::thread::scope(|s| {
964 s.spawn(|| {
965 Python::attach(|py| {
966 let b: $guard = mutex.lock_py_attached(py);
967 barrier.wait();
968 std::thread::sleep(core::time::Duration::from_millis(10));
970 (*b).bind(py).borrow().0.store(true, Ordering::Release);
971 drop(b);
972 });
973 });
974 s.spawn(|| {
975 barrier.wait();
976 Python::attach(|py| {
977 let b: $guard = mutex.lock_py_attached(py);
979 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
980 });
981 });
982 });
983 }};
984 }
985
986 test_mutex!(parking_lot::MutexGuard<'_, _>, |py| {
987 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
988 });
989
990 test_mutex!(parking_lot::ReentrantMutexGuard<'_, _>, |py| {
991 parking_lot::ReentrantMutex::new(
992 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
993 )
994 });
995
996 #[cfg(feature = "arc_lock")]
997 test_mutex!(parking_lot::ArcMutexGuard<_, _>, |py| {
998 let mutex =
999 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
1000 alloc::sync::Arc::new(mutex)
1001 });
1002
1003 #[cfg(feature = "arc_lock")]
1004 test_mutex!(parking_lot::ArcReentrantMutexGuard<_, _, _>, |py| {
1005 let mutex =
1006 parking_lot::ReentrantMutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
1007 alloc::sync::Arc::new(mutex)
1008 });
1009 }
1010
1011 #[cfg(not(target_arch = "wasm32"))] #[cfg(wip_feature_std)]
1013 #[test]
1014 fn test_mutex_ext_poison() {
1015 let mutex = Mutex::new(42);
1016
1017 std::thread::scope(|s| {
1018 let lock_result = s.spawn(|| {
1019 Python::attach(|py| {
1020 let _unused = mutex.lock_py_attached(py);
1021 panic!();
1022 });
1023 });
1024 assert!(lock_result.join().is_err());
1025 assert!(mutex.is_poisoned());
1026 });
1027 let guard = Python::attach(|py| {
1028 match mutex.lock_py_attached(py) {
1030 Ok(guard) => guard,
1031 Err(poisoned) => poisoned.into_inner(),
1032 }
1033 });
1034 assert_eq!(*guard, 42);
1035 }
1036
1037 #[cfg(feature = "macros")]
1038 #[cfg(not(target_arch = "wasm32"))] #[test]
1040 fn test_rwlock_ext_writer_blocks_reader() {
1041 use std::sync::RwLock;
1042
1043 let barrier = Barrier::new(2);
1044
1045 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
1046 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1047 });
1048
1049 std::thread::scope(|s| {
1050 s.spawn(|| {
1051 Python::attach(|py| {
1052 let b = rwlock.write_py_attached(py).unwrap();
1053 barrier.wait();
1054 std::thread::sleep(core::time::Duration::from_millis(10));
1056 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1057 drop(b);
1058 });
1059 });
1060 s.spawn(|| {
1061 barrier.wait();
1062 Python::attach(|py| {
1063 let b = rwlock.read_py_attached(py).unwrap();
1065 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1066 });
1067 });
1068 });
1069 }
1070
1071 #[cfg(feature = "macros")]
1072 #[cfg(not(target_arch = "wasm32"))] #[test]
1074 fn test_rwlock_ext_reader_blocks_writer() {
1075 use std::sync::RwLock;
1076
1077 let barrier = Barrier::new(2);
1078
1079 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
1080 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1081 });
1082
1083 std::thread::scope(|s| {
1084 s.spawn(|| {
1085 Python::attach(|py| {
1086 let b = rwlock.read_py_attached(py).unwrap();
1087 barrier.wait();
1088
1089 std::thread::sleep(core::time::Duration::from_millis(10));
1091
1092 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire));
1095 });
1096 });
1097 s.spawn(|| {
1098 barrier.wait();
1099 Python::attach(|py| {
1100 let b = rwlock.write_py_attached(py).unwrap();
1102 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1103 drop(b);
1104 });
1105 });
1106 });
1107
1108 Python::attach(|py| {
1110 let b = rwlock.read_py_attached(py).unwrap();
1111 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1112 drop(b);
1113 });
1114 }
1115
1116 #[cfg(feature = "macros")]
1117 #[cfg(all(
1118 any(feature = "parking_lot", feature = "lock_api"),
1119 not(target_arch = "wasm32") ))]
1121 #[test]
1122 fn test_parking_lot_rwlock_ext_writer_blocks_reader() {
1123 macro_rules! test_rwlock {
1124 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1125 let barrier = Barrier::new(2);
1126
1127 let rwlock = Python::attach({ $rwlock });
1128
1129 std::thread::scope(|s| {
1130 s.spawn(|| {
1131 Python::attach(|py| {
1132 let b: $write_guard = rwlock.write_py_attached(py);
1133 barrier.wait();
1134 std::thread::sleep(core::time::Duration::from_millis(10));
1136 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1137 drop(b);
1138 });
1139 });
1140 s.spawn(|| {
1141 barrier.wait();
1142 Python::attach(|py| {
1143 let b: $read_guard = rwlock.read_py_attached(py);
1145 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1146 });
1147 });
1148 });
1149 }};
1150 }
1151
1152 test_rwlock!(
1153 parking_lot::RwLockWriteGuard<'_, _>,
1154 parking_lot::RwLockReadGuard<'_, _>,
1155 |py| {
1156 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1157 }
1158 );
1159
1160 #[cfg(feature = "arc_lock")]
1161 test_rwlock!(
1162 parking_lot::ArcRwLockWriteGuard<_, _>,
1163 parking_lot::ArcRwLockReadGuard<_, _>,
1164 |py| {
1165 let rwlock = parking_lot::RwLock::new(
1166 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1167 );
1168 alloc::sync::Arc::new(rwlock)
1169 }
1170 );
1171 }
1172
1173 #[cfg(feature = "macros")]
1174 #[cfg(all(
1175 any(feature = "parking_lot", feature = "lock_api"),
1176 not(target_arch = "wasm32") ))]
1178 #[test]
1179 fn test_parking_lot_rwlock_ext_reader_blocks_writer() {
1180 macro_rules! test_rwlock {
1181 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1182 let barrier = Barrier::new(2);
1183
1184 let rwlock = Python::attach({ $rwlock });
1185
1186 std::thread::scope(|s| {
1187 s.spawn(|| {
1188 Python::attach(|py| {
1189 let b: $read_guard = rwlock.read_py_attached(py);
1190 barrier.wait();
1191
1192 std::thread::sleep(core::time::Duration::from_millis(10));
1194
1195 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire)); (*b).bind(py).borrow().0.store(true, Ordering::Release);
1198
1199 drop(b);
1200 });
1201 });
1202 s.spawn(|| {
1203 barrier.wait();
1204 Python::attach(|py| {
1205 let b: $write_guard = rwlock.write_py_attached(py);
1207 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1208 });
1209 });
1210 });
1211
1212 Python::attach(|py| {
1214 let b: $read_guard = rwlock.read_py_attached(py);
1215 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1216 drop(b);
1217 });
1218 }};
1219 }
1220
1221 test_rwlock!(
1222 parking_lot::RwLockWriteGuard<'_, _>,
1223 parking_lot::RwLockReadGuard<'_, _>,
1224 |py| {
1225 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1226 }
1227 );
1228
1229 #[cfg(feature = "arc_lock")]
1230 test_rwlock!(
1231 parking_lot::ArcRwLockWriteGuard<_, _>,
1232 parking_lot::ArcRwLockReadGuard<_, _>,
1233 |py| {
1234 let rwlock = parking_lot::RwLock::new(
1235 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1236 );
1237 alloc::sync::Arc::new(rwlock)
1238 }
1239 );
1240 }
1241
1242 #[cfg(not(target_arch = "wasm32"))] #[test]
1244 fn test_rwlock_ext_poison() {
1245 use std::sync::RwLock;
1246
1247 let rwlock = RwLock::new(42);
1248
1249 std::thread::scope(|s| {
1250 let lock_result = s.spawn(|| {
1251 Python::attach(|py| {
1252 let _unused = rwlock.write_py_attached(py);
1253 panic!();
1254 });
1255 });
1256 assert!(lock_result.join().is_err());
1257 assert!(rwlock.is_poisoned());
1258 Python::attach(|py| {
1259 assert!(rwlock.read_py_attached(py).is_err());
1260 assert!(rwlock.write_py_attached(py).is_err());
1261 });
1262 });
1263 Python::attach(|py| {
1264 let guard = rwlock.write_py_attached(py).unwrap_err().into_inner();
1266 assert_eq!(*guard, 42);
1267 });
1268 }
1269}