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