1use crate::{
13 internal::state::SuspendAttach,
14 sealed::Sealed,
15 types::{PyAny, PyString},
16 Bound, Py, Python,
17};
18use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit};
19use std::sync::{Once, OnceState};
20
21pub mod critical_section;
22pub(crate) mod once_lock;
23
24#[deprecated(
26 since = "0.28.0",
27 note = "use pyo3::sync::critical_section::with_critical_section instead"
28)]
29pub fn with_critical_section<F, R>(object: &Bound<'_, PyAny>, f: F) -> R
30where
31 F: FnOnce() -> R,
32{
33 crate::sync::critical_section::with_critical_section(object, f)
34}
35
36#[deprecated(
38 since = "0.28.0",
39 note = "use pyo3::sync::critical_section::with_critical_section2 instead"
40)]
41pub fn with_critical_section2<F, R>(a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>, f: F) -> R
42where
43 F: FnOnce() -> R,
44{
45 crate::sync::critical_section::with_critical_section2(a, b, f)
46}
47pub use self::once_lock::PyOnceLock;
48
49#[deprecated(
50 since = "0.26.0",
51 note = "Now internal only, to be removed after https://github.com/PyO3/pyo3/pull/5341"
52)]
53pub(crate) struct GILOnceCell<T> {
54 once: Once,
55 data: UnsafeCell<MaybeUninit<T>>,
56
57 _marker: PhantomData<T>,
79}
80
81#[allow(deprecated)]
82impl<T> Default for GILOnceCell<T> {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88#[allow(deprecated)]
92unsafe impl<T: Send + Sync> Sync for GILOnceCell<T> {}
93#[allow(deprecated)]
94unsafe impl<T: Send> Send for GILOnceCell<T> {}
95
96#[allow(deprecated)]
97impl<T> GILOnceCell<T> {
98 pub const fn new() -> Self {
100 Self {
101 once: Once::new(),
102 data: UnsafeCell::new(MaybeUninit::uninit()),
103 _marker: PhantomData,
104 }
105 }
106
107 #[inline]
109 pub fn get(&self, _py: Python<'_>) -> Option<&T> {
110 if self.once.is_completed() {
111 Some(unsafe { (*self.data.get()).assume_init_ref() })
113 } else {
114 None
115 }
116 }
117
118 #[inline]
123 pub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
124 where
125 F: FnOnce() -> Result<T, E>,
126 {
127 if let Some(value) = self.get(py) {
128 return Ok(value);
129 }
130
131 self.init(py, f)
132 }
133
134 #[cold]
135 fn init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
136 where
137 F: FnOnce() -> Result<T, E>,
138 {
139 let value = f()?;
147 let _ = self.set(py, value);
148
149 Ok(self.get(py).unwrap())
150 }
151
152 pub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T> {
157 let mut value = Some(value);
158 self.once.call_once_force(|_| {
162 unsafe {
165 (*self.data.get()).write(value.take().unwrap());
167 }
168 });
169
170 match value {
171 Some(value) => Err(value),
173 None => Ok(()),
174 }
175 }
176}
177
178#[allow(deprecated)]
179impl<T> Drop for GILOnceCell<T> {
180 fn drop(&mut self) {
181 if self.once.is_completed() {
182 unsafe { MaybeUninit::assume_init_drop(self.data.get_mut()) }
184 }
185 }
186}
187
188#[macro_export]
226macro_rules! intern {
227 ($py: expr, $text: expr) => {{
228 static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text);
229 INTERNED.get($py)
230 }};
231}
232
233#[doc(hidden)]
235pub struct Interned(&'static str, PyOnceLock<Py<PyString>>);
236
237impl Interned {
238 pub const fn new(value: &'static str) -> Self {
240 Interned(value, PyOnceLock::new())
241 }
242
243 #[inline]
245 pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> {
246 self.1
247 .get_or_init(py, || PyString::intern(py, self.0).into())
248 .bind(py)
249 }
250}
251
252pub trait OnceExt: Sealed {
255 type OnceState;
257
258 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce());
261
262 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&Self::OnceState));
265}
266
267pub trait OnceLockExt<T>: once_lock_ext_sealed::Sealed {
270 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
280 where
281 F: FnOnce() -> T;
282}
283
284pub trait MutexExt<T>: Sealed {
287 type LockResult<'a>
289 where
290 Self: 'a;
291
292 fn lock_py_attached(&self, py: Python<'_>) -> Self::LockResult<'_>;
300}
301
302pub trait RwLockExt<T>: rwlock_ext_sealed::Sealed {
305 type ReadLockResult<'a>
307 where
308 Self: 'a;
309
310 type WriteLockResult<'a>
312 where
313 Self: 'a;
314
315 fn read_py_attached(&self, py: Python<'_>) -> Self::ReadLockResult<'_>;
324
325 fn write_py_attached(&self, py: Python<'_>) -> Self::WriteLockResult<'_>;
334}
335
336impl OnceExt for Once {
337 type OnceState = OnceState;
338
339 fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce()) {
340 if self.is_completed() {
341 return;
342 }
343
344 init_once_py_attached(self, py, f)
345 }
346
347 fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&OnceState)) {
348 if self.is_completed() {
349 return;
350 }
351
352 init_once_force_py_attached(self, py, f);
353 }
354}
355
356#[cfg(feature = "parking_lot")]
357impl OnceExt for parking_lot::Once {
358 type OnceState = parking_lot::OnceState;
359
360 fn call_once_py_attached(&self, _py: Python<'_>, f: impl FnOnce()) {
361 if self.state().done() {
362 return;
363 }
364
365 let ts_guard = unsafe { SuspendAttach::new() };
366
367 self.call_once(move || {
368 drop(ts_guard);
369 f();
370 });
371 }
372
373 fn call_once_force_py_attached(
374 &self,
375 _py: Python<'_>,
376 f: impl FnOnce(&parking_lot::OnceState),
377 ) {
378 if self.state().done() {
379 return;
380 }
381
382 let ts_guard = unsafe { SuspendAttach::new() };
383
384 self.call_once_force(move |state| {
385 drop(ts_guard);
386 f(&state);
387 });
388 }
389}
390
391impl<T> OnceLockExt<T> for std::sync::OnceLock<T> {
392 fn get_or_init_py_attached<F>(&self, py: Python<'_>, f: F) -> &T
393 where
394 F: FnOnce() -> T,
395 {
396 self.get()
398 .unwrap_or_else(|| init_once_lock_py_attached(self, py, f))
399 }
400}
401
402impl<T> MutexExt<T> for std::sync::Mutex<T> {
403 type LockResult<'a>
404 = std::sync::LockResult<std::sync::MutexGuard<'a, T>>
405 where
406 Self: 'a;
407
408 fn lock_py_attached(
409 &self,
410 _py: Python<'_>,
411 ) -> std::sync::LockResult<std::sync::MutexGuard<'_, T>> {
412 match self.try_lock() {
417 Ok(inner) => return Ok(inner),
418 Err(std::sync::TryLockError::Poisoned(inner)) => {
419 return std::sync::LockResult::Err(inner)
420 }
421 Err(std::sync::TryLockError::WouldBlock) => {}
422 }
423 let ts_guard = unsafe { SuspendAttach::new() };
427 let res = self.lock();
428 drop(ts_guard);
429 res
430 }
431}
432
433#[cfg(feature = "lock_api")]
434impl<R: lock_api::RawMutex, T> MutexExt<T> for lock_api::Mutex<R, T> {
435 type LockResult<'a>
436 = lock_api::MutexGuard<'a, R, T>
437 where
438 Self: 'a;
439
440 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::MutexGuard<'_, R, T> {
441 if let Some(guard) = self.try_lock() {
442 return guard;
443 }
444
445 let ts_guard = unsafe { SuspendAttach::new() };
446 let res = self.lock();
447 drop(ts_guard);
448 res
449 }
450}
451
452#[cfg(feature = "arc_lock")]
453impl<R, T> MutexExt<T> for alloc::sync::Arc<lock_api::Mutex<R, T>>
454where
455 R: lock_api::RawMutex,
456{
457 type LockResult<'a>
458 = lock_api::ArcMutexGuard<R, T>
459 where
460 Self: 'a;
461
462 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcMutexGuard<R, T> {
463 if let Some(guard) = self.try_lock_arc() {
464 return guard;
465 }
466
467 let ts_guard = unsafe { SuspendAttach::new() };
468 let res = self.lock_arc();
469 drop(ts_guard);
470 res
471 }
472}
473
474#[cfg(feature = "lock_api")]
475impl<R, G, T> MutexExt<T> for lock_api::ReentrantMutex<R, G, T>
476where
477 R: lock_api::RawMutex,
478 G: lock_api::GetThreadId,
479{
480 type LockResult<'a>
481 = lock_api::ReentrantMutexGuard<'a, R, G, T>
482 where
483 Self: 'a;
484
485 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ReentrantMutexGuard<'_, R, G, T> {
486 if let Some(guard) = self.try_lock() {
487 return guard;
488 }
489
490 let ts_guard = unsafe { SuspendAttach::new() };
491 let res = self.lock();
492 drop(ts_guard);
493 res
494 }
495}
496
497#[cfg(feature = "arc_lock")]
498impl<R, G, T> MutexExt<T> for alloc::sync::Arc<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::ArcReentrantMutexGuard<R, G, T>
505 where
506 Self: 'a;
507
508 fn lock_py_attached(&self, _py: Python<'_>) -> lock_api::ArcReentrantMutexGuard<R, G, T> {
509 if let Some(guard) = self.try_lock_arc() {
510 return guard;
511 }
512
513 let ts_guard = unsafe { SuspendAttach::new() };
514 let res = self.lock_arc();
515 drop(ts_guard);
516 res
517 }
518}
519
520impl<T> RwLockExt<T> for std::sync::RwLock<T> {
521 type ReadLockResult<'a>
522 = std::sync::LockResult<std::sync::RwLockReadGuard<'a, T>>
523 where
524 Self: 'a;
525
526 type WriteLockResult<'a>
527 = std::sync::LockResult<std::sync::RwLockWriteGuard<'a, T>>
528 where
529 Self: 'a;
530
531 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
532 match self.try_read() {
537 Ok(inner) => return Ok(inner),
538 Err(std::sync::TryLockError::Poisoned(inner)) => {
539 return std::sync::LockResult::Err(inner)
540 }
541 Err(std::sync::TryLockError::WouldBlock) => {}
542 }
543
544 let ts_guard = unsafe { SuspendAttach::new() };
548
549 let res = self.read();
550 drop(ts_guard);
551 res
552 }
553
554 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
555 match self.try_write() {
560 Ok(inner) => return Ok(inner),
561 Err(std::sync::TryLockError::Poisoned(inner)) => {
562 return std::sync::LockResult::Err(inner)
563 }
564 Err(std::sync::TryLockError::WouldBlock) => {}
565 }
566
567 let ts_guard = unsafe { SuspendAttach::new() };
571
572 let res = self.write();
573 drop(ts_guard);
574 res
575 }
576}
577
578#[cfg(feature = "lock_api")]
579impl<R: lock_api::RawRwLock, T> RwLockExt<T> for lock_api::RwLock<R, T> {
580 type ReadLockResult<'a>
581 = lock_api::RwLockReadGuard<'a, R, T>
582 where
583 Self: 'a;
584
585 type WriteLockResult<'a>
586 = lock_api::RwLockWriteGuard<'a, R, T>
587 where
588 Self: 'a;
589
590 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
591 if let Some(guard) = self.try_read() {
592 return guard;
593 }
594
595 let ts_guard = unsafe { SuspendAttach::new() };
596 let res = self.read();
597 drop(ts_guard);
598 res
599 }
600
601 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
602 if let Some(guard) = self.try_write() {
603 return guard;
604 }
605
606 let ts_guard = unsafe { SuspendAttach::new() };
607 let res = self.write();
608 drop(ts_guard);
609 res
610 }
611}
612
613#[cfg(feature = "arc_lock")]
614impl<R, T> RwLockExt<T> for alloc::sync::Arc<lock_api::RwLock<R, T>>
615where
616 R: lock_api::RawRwLock,
617{
618 type ReadLockResult<'a>
619 = lock_api::ArcRwLockReadGuard<R, T>
620 where
621 Self: 'a;
622
623 type WriteLockResult<'a>
624 = lock_api::ArcRwLockWriteGuard<R, T>
625 where
626 Self: 'a;
627
628 fn read_py_attached(&self, _py: Python<'_>) -> Self::ReadLockResult<'_> {
629 if let Some(guard) = self.try_read_arc() {
630 return guard;
631 }
632
633 let ts_guard = unsafe { SuspendAttach::new() };
634 let res = self.read_arc();
635 drop(ts_guard);
636 res
637 }
638
639 fn write_py_attached(&self, _py: Python<'_>) -> Self::WriteLockResult<'_> {
640 if let Some(guard) = self.try_write_arc() {
641 return guard;
642 }
643
644 let ts_guard = unsafe { SuspendAttach::new() };
645 let res = self.write_arc();
646 drop(ts_guard);
647 res
648 }
649}
650
651#[cold]
652fn init_once_py_attached<F, T>(once: &Once, _py: Python<'_>, f: F)
653where
654 F: FnOnce() -> T,
655{
656 let ts_guard = unsafe { SuspendAttach::new() };
660
661 once.call_once(move || {
662 drop(ts_guard);
663 f();
664 });
665}
666
667#[cold]
668fn init_once_force_py_attached<F, T>(once: &Once, _py: Python<'_>, f: F)
669where
670 F: FnOnce(&OnceState) -> T,
671{
672 let ts_guard = unsafe { SuspendAttach::new() };
676
677 once.call_once_force(move |state| {
678 drop(ts_guard);
679 f(state);
680 });
681}
682
683#[cold]
684fn init_once_lock_py_attached<'a, F, T>(
685 lock: &'a std::sync::OnceLock<T>,
686 _py: Python<'_>,
687 f: F,
688) -> &'a T
689where
690 F: FnOnce() -> T,
691{
692 let ts_guard = unsafe { SuspendAttach::new() };
696
697 let value = lock.get_or_init(move || {
700 drop(ts_guard);
701 f()
702 });
703
704 value
705}
706
707mod once_lock_ext_sealed {
708 pub trait Sealed {}
709 impl<T> Sealed for std::sync::OnceLock<T> {}
710}
711
712mod rwlock_ext_sealed {
713 pub trait Sealed {}
714 impl<T> Sealed for std::sync::RwLock<T> {}
715 #[cfg(feature = "lock_api")]
716 impl<R, T> Sealed for lock_api::RwLock<R, T> {}
717 #[cfg(feature = "arc_lock")]
718 impl<R, T> Sealed for alloc::sync::Arc<lock_api::RwLock<R, T>> {}
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724
725 use crate::types::{PyAnyMethods, PyDict, PyDictMethods};
726 #[cfg(not(target_arch = "wasm32"))]
727 #[cfg(feature = "macros")]
728 use core::sync::atomic::{AtomicBool, Ordering};
729 #[cfg(not(target_arch = "wasm32"))]
730 #[cfg(feature = "macros")]
731 use std::sync::Barrier;
732 #[cfg(not(target_arch = "wasm32"))]
733 use std::sync::Mutex;
734
735 #[cfg(not(target_arch = "wasm32"))]
736 #[cfg(feature = "macros")]
737 #[crate::pyclass(crate = "crate")]
738 struct BoolWrapper(AtomicBool);
739
740 #[test]
741 fn test_intern() {
742 Python::attach(|py| {
743 let foo1 = "foo";
744 let foo2 = intern!(py, "foo");
745 let foo3 = intern!(py, stringify!(foo));
746
747 let dict = PyDict::new(py);
748 dict.set_item(foo1, 42_usize).unwrap();
749 assert!(dict.contains(foo2).unwrap());
750 assert_eq!(
751 dict.get_item(foo3)
752 .unwrap()
753 .unwrap()
754 .extract::<usize>()
755 .unwrap(),
756 42
757 );
758 });
759 }
760
761 #[test]
762 #[allow(deprecated)]
763 fn test_once_cell() {
764 Python::attach(|py| {
765 let cell = GILOnceCell::new();
766
767 assert!(cell.get(py).is_none());
768
769 assert_eq!(cell.get_or_try_init(py, || Err(5)), Err(5));
770 assert!(cell.get(py).is_none());
771
772 assert_eq!(cell.get_or_try_init(py, || Ok::<_, ()>(2)), Ok(&2));
773 assert_eq!(cell.get(py), Some(&2));
774
775 assert_eq!(cell.get_or_try_init(py, || Err(5)), Ok(&2));
776 })
777 }
778
779 #[test]
780 #[allow(deprecated)]
781 fn test_once_cell_drop() {
782 #[derive(Debug)]
783 struct RecordDrop<'a>(&'a mut bool);
784
785 impl Drop for RecordDrop<'_> {
786 fn drop(&mut self) {
787 *self.0 = true;
788 }
789 }
790
791 Python::attach(|py| {
792 let mut dropped = false;
793 let cell = GILOnceCell::new();
794 cell.set(py, RecordDrop(&mut dropped)).unwrap();
795 let drop_container = cell.get(py).unwrap();
796
797 assert!(!*drop_container.0);
798 drop(cell);
799 assert!(dropped);
800 });
801 }
802
803 #[test]
804 #[cfg(not(target_arch = "wasm32"))] fn test_once_ext() {
806 macro_rules! test_once {
807 ($once:expr, $is_poisoned:expr) => {{
808 let init = $once;
810 std::thread::scope(|s| {
811 let handle = s.spawn(|| {
813 Python::attach(|py| {
814 init.call_once_py_attached(py, || panic!());
815 })
816 });
817 assert!(handle.join().is_err());
818
819 let handle = s.spawn(|| {
821 Python::attach(|py| {
822 init.call_once_py_attached(py, || {});
823 });
824 });
825
826 assert!(handle.join().is_err());
827
828 Python::attach(|py| {
830 init.call_once_force_py_attached(py, |state| {
831 assert!($is_poisoned(state.clone()));
832 });
833
834 init.call_once_py_attached(py, || {});
836 });
837
838 Python::attach(|py| init.call_once_force_py_attached(py, |_| panic!()));
840 });
841 }};
842 }
843
844 test_once!(Once::new(), OnceState::is_poisoned);
845 #[cfg(feature = "parking_lot")]
846 test_once!(parking_lot::Once::new(), parking_lot::OnceState::poisoned);
847 }
848
849 #[cfg(not(target_arch = "wasm32"))] #[test]
851 fn test_once_lock_ext() {
852 let cell = std::sync::OnceLock::new();
853 std::thread::scope(|s| {
854 assert!(cell.get().is_none());
855
856 s.spawn(|| {
857 Python::attach(|py| {
858 assert_eq!(*cell.get_or_init_py_attached(py, || 12345), 12345);
859 });
860 });
861 });
862 assert_eq!(cell.get(), Some(&12345));
863 }
864
865 #[cfg(feature = "macros")]
866 #[cfg(not(target_arch = "wasm32"))] #[test]
868 fn test_mutex_ext() {
869 let barrier = Barrier::new(2);
870
871 let mutex = Python::attach(|py| -> Mutex<Py<BoolWrapper>> {
872 Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
873 });
874
875 std::thread::scope(|s| {
876 s.spawn(|| {
877 Python::attach(|py| {
878 let b = mutex.lock_py_attached(py).unwrap();
879 barrier.wait();
880 std::thread::sleep(core::time::Duration::from_millis(10));
882 (*b).bind(py).borrow().0.store(true, Ordering::Release);
883 drop(b);
884 });
885 });
886 s.spawn(|| {
887 barrier.wait();
888 Python::attach(|py| {
889 let b = mutex.lock_py_attached(py).unwrap();
891 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
892 });
893 });
894 });
895 }
896
897 #[cfg(feature = "macros")]
898 #[cfg(all(
899 any(feature = "parking_lot", feature = "lock_api"),
900 not(target_arch = "wasm32") ))]
902 #[test]
903 fn test_parking_lot_mutex_ext() {
904 macro_rules! test_mutex {
905 ($guard:ty ,$mutex:stmt) => {{
906 let barrier = Barrier::new(2);
907
908 let mutex = Python::attach({ $mutex });
909
910 std::thread::scope(|s| {
911 s.spawn(|| {
912 Python::attach(|py| {
913 let b: $guard = mutex.lock_py_attached(py);
914 barrier.wait();
915 std::thread::sleep(core::time::Duration::from_millis(10));
917 (*b).bind(py).borrow().0.store(true, Ordering::Release);
918 drop(b);
919 });
920 });
921 s.spawn(|| {
922 barrier.wait();
923 Python::attach(|py| {
924 let b: $guard = mutex.lock_py_attached(py);
926 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
927 });
928 });
929 });
930 }};
931 }
932
933 test_mutex!(parking_lot::MutexGuard<'_, _>, |py| {
934 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
935 });
936
937 test_mutex!(parking_lot::ReentrantMutexGuard<'_, _>, |py| {
938 parking_lot::ReentrantMutex::new(
939 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
940 )
941 });
942
943 #[cfg(feature = "arc_lock")]
944 test_mutex!(parking_lot::ArcMutexGuard<_, _>, |py| {
945 let mutex =
946 parking_lot::Mutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
947 alloc::sync::Arc::new(mutex)
948 });
949
950 #[cfg(feature = "arc_lock")]
951 test_mutex!(parking_lot::ArcReentrantMutexGuard<_, _, _>, |py| {
952 let mutex =
953 parking_lot::ReentrantMutex::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap());
954 alloc::sync::Arc::new(mutex)
955 });
956 }
957
958 #[cfg(not(target_arch = "wasm32"))] #[test]
960 fn test_mutex_ext_poison() {
961 let mutex = Mutex::new(42);
962
963 std::thread::scope(|s| {
964 let lock_result = s.spawn(|| {
965 Python::attach(|py| {
966 let _unused = mutex.lock_py_attached(py);
967 panic!();
968 });
969 });
970 assert!(lock_result.join().is_err());
971 assert!(mutex.is_poisoned());
972 });
973 let guard = Python::attach(|py| {
974 match mutex.lock_py_attached(py) {
976 Ok(guard) => guard,
977 Err(poisoned) => poisoned.into_inner(),
978 }
979 });
980 assert_eq!(*guard, 42);
981 }
982
983 #[cfg(feature = "macros")]
984 #[cfg(not(target_arch = "wasm32"))] #[test]
986 fn test_rwlock_ext_writer_blocks_reader() {
987 use std::sync::RwLock;
988
989 let barrier = Barrier::new(2);
990
991 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
992 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
993 });
994
995 std::thread::scope(|s| {
996 s.spawn(|| {
997 Python::attach(|py| {
998 let b = rwlock.write_py_attached(py).unwrap();
999 barrier.wait();
1000 std::thread::sleep(core::time::Duration::from_millis(10));
1002 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1003 drop(b);
1004 });
1005 });
1006 s.spawn(|| {
1007 barrier.wait();
1008 Python::attach(|py| {
1009 let b = rwlock.read_py_attached(py).unwrap();
1011 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1012 });
1013 });
1014 });
1015 }
1016
1017 #[cfg(feature = "macros")]
1018 #[cfg(not(target_arch = "wasm32"))] #[test]
1020 fn test_rwlock_ext_reader_blocks_writer() {
1021 use std::sync::RwLock;
1022
1023 let barrier = Barrier::new(2);
1024
1025 let rwlock = Python::attach(|py| -> RwLock<Py<BoolWrapper>> {
1026 RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1027 });
1028
1029 std::thread::scope(|s| {
1030 s.spawn(|| {
1031 Python::attach(|py| {
1032 let b = rwlock.read_py_attached(py).unwrap();
1033 barrier.wait();
1034
1035 std::thread::sleep(core::time::Duration::from_millis(10));
1037
1038 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire));
1041 });
1042 });
1043 s.spawn(|| {
1044 barrier.wait();
1045 Python::attach(|py| {
1046 let b = rwlock.write_py_attached(py).unwrap();
1048 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1049 drop(b);
1050 });
1051 });
1052 });
1053
1054 Python::attach(|py| {
1056 let b = rwlock.read_py_attached(py).unwrap();
1057 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1058 drop(b);
1059 });
1060 }
1061
1062 #[cfg(feature = "macros")]
1063 #[cfg(all(
1064 any(feature = "parking_lot", feature = "lock_api"),
1065 not(target_arch = "wasm32") ))]
1067 #[test]
1068 fn test_parking_lot_rwlock_ext_writer_blocks_reader() {
1069 macro_rules! test_rwlock {
1070 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1071 let barrier = Barrier::new(2);
1072
1073 let rwlock = Python::attach({ $rwlock });
1074
1075 std::thread::scope(|s| {
1076 s.spawn(|| {
1077 Python::attach(|py| {
1078 let b: $write_guard = rwlock.write_py_attached(py);
1079 barrier.wait();
1080 std::thread::sleep(core::time::Duration::from_millis(10));
1082 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1083 drop(b);
1084 });
1085 });
1086 s.spawn(|| {
1087 barrier.wait();
1088 Python::attach(|py| {
1089 let b: $read_guard = rwlock.read_py_attached(py);
1091 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1092 });
1093 });
1094 });
1095 }};
1096 }
1097
1098 test_rwlock!(
1099 parking_lot::RwLockWriteGuard<'_, _>,
1100 parking_lot::RwLockReadGuard<'_, _>,
1101 |py| {
1102 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1103 }
1104 );
1105
1106 #[cfg(feature = "arc_lock")]
1107 test_rwlock!(
1108 parking_lot::ArcRwLockWriteGuard<_, _>,
1109 parking_lot::ArcRwLockReadGuard<_, _>,
1110 |py| {
1111 let rwlock = parking_lot::RwLock::new(
1112 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1113 );
1114 alloc::sync::Arc::new(rwlock)
1115 }
1116 );
1117 }
1118
1119 #[cfg(feature = "macros")]
1120 #[cfg(all(
1121 any(feature = "parking_lot", feature = "lock_api"),
1122 not(target_arch = "wasm32") ))]
1124 #[test]
1125 fn test_parking_lot_rwlock_ext_reader_blocks_writer() {
1126 macro_rules! test_rwlock {
1127 ($write_guard:ty, $read_guard:ty, $rwlock:stmt) => {{
1128 let barrier = Barrier::new(2);
1129
1130 let rwlock = Python::attach({ $rwlock });
1131
1132 std::thread::scope(|s| {
1133 s.spawn(|| {
1134 Python::attach(|py| {
1135 let b: $read_guard = rwlock.read_py_attached(py);
1136 barrier.wait();
1137
1138 std::thread::sleep(core::time::Duration::from_millis(10));
1140
1141 assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire)); (*b).bind(py).borrow().0.store(true, Ordering::Release);
1144
1145 drop(b);
1146 });
1147 });
1148 s.spawn(|| {
1149 barrier.wait();
1150 Python::attach(|py| {
1151 let b: $write_guard = rwlock.write_py_attached(py);
1153 (*b).bind(py).borrow().0.store(true, Ordering::Release);
1154 });
1155 });
1156 });
1157
1158 Python::attach(|py| {
1160 let b: $read_guard = rwlock.read_py_attached(py);
1161 assert!((*b).bind(py).borrow().0.load(Ordering::Acquire));
1162 drop(b);
1163 });
1164 }};
1165 }
1166
1167 test_rwlock!(
1168 parking_lot::RwLockWriteGuard<'_, _>,
1169 parking_lot::RwLockReadGuard<'_, _>,
1170 |py| {
1171 parking_lot::RwLock::new(Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap())
1172 }
1173 );
1174
1175 #[cfg(feature = "arc_lock")]
1176 test_rwlock!(
1177 parking_lot::ArcRwLockWriteGuard<_, _>,
1178 parking_lot::ArcRwLockReadGuard<_, _>,
1179 |py| {
1180 let rwlock = parking_lot::RwLock::new(
1181 Py::new(py, BoolWrapper(AtomicBool::new(false))).unwrap(),
1182 );
1183 alloc::sync::Arc::new(rwlock)
1184 }
1185 );
1186 }
1187
1188 #[cfg(not(target_arch = "wasm32"))] #[test]
1190 fn test_rwlock_ext_poison() {
1191 use std::sync::RwLock;
1192
1193 let rwlock = RwLock::new(42);
1194
1195 std::thread::scope(|s| {
1196 let lock_result = s.spawn(|| {
1197 Python::attach(|py| {
1198 let _unused = rwlock.write_py_attached(py);
1199 panic!();
1200 });
1201 });
1202 assert!(lock_result.join().is_err());
1203 assert!(rwlock.is_poisoned());
1204 Python::attach(|py| {
1205 assert!(rwlock.read_py_attached(py).is_err());
1206 assert!(rwlock.write_py_attached(py).is_err());
1207 });
1208 });
1209 Python::attach(|py| {
1210 let guard = rwlock.write_py_attached(py).unwrap_err().into_inner();
1212 assert_eq!(*guard, 42);
1213 });
1214 }
1215}