1#![cfg_attr(docsrs, feature(doc_cfg))]
47
48use std::ops::{Deref, DerefMut};
49
50#[cfg(all(
52 feature = "std-sync",
53 not(feature = "tokio-sync"),
54 not(feature = "wasm-sync")
55))]
56use std::sync::{Arc, RwLock, Weak};
57
58#[cfg(feature = "tokio-sync")]
60use std::sync::{Arc, Weak};
61#[cfg(feature = "tokio-sync")]
62use tokio::sync::RwLock;
63
64#[cfg(any(
66 feature = "wasm-sync",
67 all(
68 target_arch = "wasm32",
69 not(feature = "std-sync"),
70 not(feature = "tokio-sync")
71 )
72))]
73use std::cell::{Ref, RefCell, RefMut};
74#[cfg(any(
75 feature = "wasm-sync",
76 all(
77 target_arch = "wasm32",
78 not(feature = "std-sync"),
79 not(feature = "tokio-sync")
80 )
81))]
82use std::rc::{Rc, Weak as RcWeak};
83
84#[derive(Debug)]
94pub struct SharedContainer<T> {
95 #[cfg(all(
97 feature = "std-sync",
98 not(feature = "tokio-sync"),
99 not(feature = "wasm-sync")
100 ))]
101 std_inner: Arc<RwLock<T>>,
102
103 #[cfg(feature = "tokio-sync")]
105 tokio_inner: Arc<RwLock<T>>,
106
107 #[cfg(any(
109 feature = "wasm-sync",
110 all(
111 target_arch = "wasm32",
112 not(feature = "std-sync"),
113 not(feature = "tokio-sync")
114 )
115 ))]
116 wasm_inner: Rc<RefCell<T>>,
117}
118
119#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
121unsafe impl<T: Send> Send for SharedContainer<T> {}
122
123#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
124unsafe impl<T: Send + Sync> Sync for SharedContainer<T> {}
125
126#[derive(Debug)]
136pub struct WeakSharedContainer<T> {
137 #[cfg(all(
139 feature = "std-sync",
140 not(feature = "tokio-sync"),
141 not(feature = "wasm-sync")
142 ))]
143 std_inner: Weak<RwLock<T>>,
144
145 #[cfg(feature = "tokio-sync")]
147 tokio_inner: Weak<RwLock<T>>,
148
149 #[cfg(any(
151 feature = "wasm-sync",
152 all(
153 target_arch = "wasm32",
154 not(feature = "std-sync"),
155 not(feature = "tokio-sync")
156 )
157 ))]
158 wasm_inner: RcWeak<RefCell<T>>,
159}
160
161impl<T> Clone for WeakSharedContainer<T> {
162 fn clone(&self) -> Self {
163 #[cfg(all(
165 feature = "std-sync",
166 not(feature = "tokio-sync"),
167 not(feature = "wasm-sync")
168 ))]
169 {
170 WeakSharedContainer {
171 std_inner: self.std_inner.clone(),
172 }
173 }
174
175 #[cfg(feature = "tokio-sync")]
176 {
177 WeakSharedContainer {
178 tokio_inner: self.tokio_inner.clone(),
179 }
180 }
181
182 #[cfg(any(
183 feature = "wasm-sync",
184 all(
185 target_arch = "wasm32",
186 not(feature = "std-sync"),
187 not(feature = "tokio-sync")
188 )
189 ))]
190 {
191 WeakSharedContainer {
192 wasm_inner: self.wasm_inner.clone(),
193 }
194 }
195 }
196}
197
198impl<T: PartialEq> PartialEq for SharedContainer<T> {
199 fn eq(&self, other: &Self) -> bool {
200 #[cfg(feature = "tokio-sync")]
201 {
202 use std::sync::Arc;
204 let self_inner = Arc::clone(&self.tokio_inner);
205 let other_inner = Arc::clone(&other.tokio_inner);
206
207 tokio::task::block_in_place(|| {
208 let rt = tokio::runtime::Builder::new_current_thread()
210 .enable_all()
211 .build()
212 .unwrap();
213 rt.block_on(async {
214 let self_val = self_inner.read().await;
215 let other_val = other_inner.read().await;
216 *self_val == *other_val
217 })
218 })
219 }
220
221 #[cfg(not(feature = "tokio-sync"))]
222 {
223 match (self.read(), other.read()) {
224 (Some(self_val), Some(other_val)) => *self_val == *other_val,
225 _ => false,
226 }
227 }
228 }
229}
230
231impl<T> Clone for SharedContainer<T> {
232 fn clone(&self) -> Self {
233 #[cfg(all(
234 feature = "std-sync",
235 not(feature = "tokio-sync"),
236 not(feature = "wasm-sync")
237 ))]
238 {
239 SharedContainer {
240 std_inner: Arc::clone(&self.std_inner),
241 }
242 }
243
244 #[cfg(feature = "tokio-sync")]
245 {
246 SharedContainer {
247 tokio_inner: Arc::clone(&self.tokio_inner),
248 }
249 }
250
251 #[cfg(any(
252 feature = "wasm-sync",
253 all(
254 target_arch = "wasm32",
255 not(feature = "std-sync"),
256 not(feature = "tokio-sync")
257 )
258 ))]
259 {
260 SharedContainer {
261 wasm_inner: Rc::clone(&self.wasm_inner),
262 }
263 }
264 }
265}
266
267impl<T: Clone> SharedContainer<T> {
268 pub fn get_cloned(&self) -> Option<T> {
281 #[cfg(feature = "tokio-sync")]
282 {
283 use std::sync::Arc;
287 let inner = Arc::clone(&self.tokio_inner);
288 let value = tokio::task::block_in_place(|| {
289 let rt = tokio::runtime::Builder::new_current_thread()
291 .enable_all()
292 .build()
293 .unwrap();
294 rt.block_on(async {
295 let guard = inner.read().await;
296 (*guard).clone()
297 })
298 });
299 Some(value)
300 }
301
302 #[cfg(not(feature = "tokio-sync"))]
303 {
304 let guard = self.read()?;
305 Some((*guard).clone())
306 }
307 }
308
309 #[cfg(feature = "tokio-sync")]
328 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
329 pub async fn get_cloned_async(&self) -> T
330 where
331 T: Clone,
332 {
333 let guard = self.tokio_inner.read().await;
334 (*guard).clone()
335 }
336}
337
338impl<T> SharedContainer<T> {
339 pub fn new(value: T) -> Self {
347 #[cfg(all(
348 feature = "std-sync",
349 not(feature = "tokio-sync"),
350 not(feature = "wasm-sync")
351 ))]
352 {
353 SharedContainer {
354 std_inner: Arc::new(RwLock::new(value)),
355 }
356 }
357
358 #[cfg(feature = "tokio-sync")]
359 {
360 SharedContainer {
361 tokio_inner: Arc::new(RwLock::new(value)),
362 }
363 }
364
365 #[cfg(any(
366 feature = "wasm-sync",
367 all(
368 target_arch = "wasm32",
369 not(feature = "std-sync"),
370 not(feature = "tokio-sync")
371 )
372 ))]
373 {
374 SharedContainer {
375 wasm_inner: Rc::new(RefCell::new(value)),
376 }
377 }
378 }
379
380 pub fn read(&self) -> Option<SharedReadGuard<T>> {
390 #[cfg(all(
391 feature = "std-sync",
392 not(feature = "tokio-sync"),
393 not(feature = "wasm-sync")
394 ))]
395 {
396 match self.std_inner.read() {
397 Ok(guard) => Some(SharedReadGuard::StdSync(guard)),
398 Err(_) => None,
399 }
400 }
401
402 #[cfg(feature = "tokio-sync")]
403 {
404 None
407 }
408
409 #[cfg(any(
410 feature = "wasm-sync",
411 all(
412 target_arch = "wasm32",
413 not(feature = "std-sync"),
414 not(feature = "tokio-sync")
415 )
416 ))]
417 {
418 match self.wasm_inner.try_borrow() {
419 Ok(borrow) => Some(SharedReadGuard::Single(borrow)),
420 Err(_) => None,
421 }
422 }
423 }
424
425 pub fn write(&self) -> Option<SharedWriteGuard<T>> {
435 #[cfg(all(
436 feature = "std-sync",
437 not(feature = "tokio-sync"),
438 not(feature = "wasm-sync")
439 ))]
440 {
441 match self.std_inner.write() {
442 Ok(guard) => Some(SharedWriteGuard::StdSync(guard)),
443 Err(_) => None,
444 }
445 }
446
447 #[cfg(feature = "tokio-sync")]
448 {
449 None
452 }
453
454 #[cfg(any(
455 feature = "wasm-sync",
456 all(
457 target_arch = "wasm32",
458 not(feature = "std-sync"),
459 not(feature = "tokio-sync")
460 )
461 ))]
462 {
463 match self.wasm_inner.try_borrow_mut() {
464 Ok(borrow) => Some(SharedWriteGuard::Single(borrow)),
465 Err(_) => None,
466 }
467 }
468 }
469
470 pub fn downgrade(&self) -> WeakSharedContainer<T> {
478 #[cfg(all(
479 feature = "std-sync",
480 not(feature = "tokio-sync"),
481 not(feature = "wasm-sync")
482 ))]
483 {
484 WeakSharedContainer {
485 std_inner: Arc::downgrade(&self.std_inner),
486 }
487 }
488
489 #[cfg(feature = "tokio-sync")]
490 {
491 WeakSharedContainer {
492 tokio_inner: Arc::downgrade(&self.tokio_inner),
493 }
494 }
495
496 #[cfg(any(
497 feature = "wasm-sync",
498 all(
499 target_arch = "wasm32",
500 not(feature = "std-sync"),
501 not(feature = "tokio-sync")
502 )
503 ))]
504 {
505 WeakSharedContainer {
506 wasm_inner: Rc::downgrade(&self.wasm_inner),
507 }
508 }
509 }
510
511 #[cfg(feature = "tokio-sync")]
530 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
531 pub async fn read_async(&self) -> SharedReadGuard<'_, T> {
532 let guard = self.tokio_inner.read().await;
533 SharedReadGuard::TokioSync(guard)
534 }
535
536 #[cfg(feature = "tokio-sync")]
555 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
556 pub async fn write_async(&self) -> SharedWriteGuard<'_, T> {
557 let guard = self.tokio_inner.write().await;
558 SharedWriteGuard::TokioSync(guard)
559 }
560}
561
562impl<T> WeakSharedContainer<T> {
563 pub fn upgrade(&self) -> Option<SharedContainer<T>> {
572 #[cfg(all(
574 feature = "std-sync",
575 not(feature = "tokio-sync"),
576 not(feature = "wasm-sync")
577 ))]
578 {
579 self.std_inner.upgrade().map(|inner| SharedContainer { std_inner: inner })
580 }
581
582 #[cfg(feature = "tokio-sync")]
583 {
584 self.tokio_inner.upgrade().map(|inner| SharedContainer { tokio_inner: inner })
585 }
586
587 #[cfg(any(
588 feature = "wasm-sync",
589 all(
590 target_arch = "wasm32",
591 not(feature = "std-sync"),
592 not(feature = "tokio-sync")
593 )
594 ))]
595 {
596 self.wasm_inner.upgrade().map(|inner| SharedContainer { wasm_inner: inner })
597 }
598 }
599}
600pub enum SharedReadGuard<'a, T> {
609 #[cfg(all(
610 feature = "std-sync",
611 not(feature = "tokio-sync"),
612 not(feature = "wasm-sync")
613 ))]
614 StdSync(std::sync::RwLockReadGuard<'a, T>),
615
616 #[cfg(feature = "tokio-sync")]
617 TokioSync(tokio::sync::RwLockReadGuard<'a, T>),
618
619 #[cfg(any(
620 feature = "wasm-sync",
621 all(
622 target_arch = "wasm32",
623 not(feature = "std-sync"),
624 not(feature = "tokio-sync")
625 )
626 ))]
627 Single(Ref<'a, T>),
628}
629
630impl<'a, T> Deref for SharedReadGuard<'a, T> {
631 type Target = T;
632
633 fn deref(&self) -> &Self::Target {
634 #[cfg(all(
635 feature = "std-sync",
636 not(feature = "tokio-sync"),
637 not(feature = "wasm-sync")
638 ))]
639 {
640 match self {
641 SharedReadGuard::StdSync(guard) => guard.deref(),
642 #[allow(unreachable_patterns)]
643 _ => unreachable!(),
644 }
645 }
646
647 #[cfg(feature = "tokio-sync")]
648 {
649 match self {
650 SharedReadGuard::TokioSync(guard) => guard.deref(),
651 #[allow(unreachable_patterns)]
652 _ => unreachable!(),
653 }
654 }
655
656 #[cfg(any(
657 feature = "wasm-sync",
658 all(
659 target_arch = "wasm32",
660 not(feature = "std-sync"),
661 not(feature = "tokio-sync")
662 )
663 ))]
664 {
665 match self {
666 SharedReadGuard::Single(borrow) => borrow.deref(),
667 #[allow(unreachable_patterns)]
668 _ => unreachable!(),
669 }
670 }
671 }
672}
673
674pub enum SharedWriteGuard<'a, T> {
683 #[cfg(all(
684 feature = "std-sync",
685 not(feature = "tokio-sync"),
686 not(feature = "wasm-sync")
687 ))]
688 StdSync(std::sync::RwLockWriteGuard<'a, T>),
689
690 #[cfg(feature = "tokio-sync")]
691 TokioSync(tokio::sync::RwLockWriteGuard<'a, T>),
692
693 #[cfg(any(
694 feature = "wasm-sync",
695 all(
696 target_arch = "wasm32",
697 not(feature = "std-sync"),
698 not(feature = "tokio-sync")
699 )
700 ))]
701 Single(RefMut<'a, T>),
702}
703
704impl<'a, T> Deref for SharedWriteGuard<'a, T> {
705 type Target = T;
706
707 fn deref(&self) -> &Self::Target {
708 #[cfg(all(
709 feature = "std-sync",
710 not(feature = "tokio-sync"),
711 not(feature = "wasm-sync")
712 ))]
713 {
714 match self {
715 SharedWriteGuard::StdSync(guard) => guard.deref(),
716 #[allow(unreachable_patterns)]
717 _ => unreachable!(),
718 }
719 }
720
721 #[cfg(feature = "tokio-sync")]
722 {
723 match self {
724 SharedWriteGuard::TokioSync(guard) => guard.deref(),
725 #[allow(unreachable_patterns)]
726 _ => unreachable!(),
727 }
728 }
729
730 #[cfg(any(
731 feature = "wasm-sync",
732 all(
733 target_arch = "wasm32",
734 not(feature = "std-sync"),
735 not(feature = "tokio-sync")
736 )
737 ))]
738 {
739 match self {
740 SharedWriteGuard::Single(borrow) => borrow.deref(),
741 #[allow(unreachable_patterns)]
742 _ => unreachable!(),
743 }
744 }
745 }
746}
747
748impl<'a, T> DerefMut for SharedWriteGuard<'a, T> {
749 fn deref_mut(&mut self) -> &mut Self::Target {
750 #[cfg(all(
751 feature = "std-sync",
752 not(feature = "tokio-sync"),
753 not(feature = "wasm-sync")
754 ))]
755 {
756 match self {
757 SharedWriteGuard::StdSync(guard) => guard.deref_mut(),
758 #[allow(unreachable_patterns)]
759 _ => unreachable!(),
760 }
761 }
762
763 #[cfg(feature = "tokio-sync")]
764 {
765 match self {
766 SharedWriteGuard::TokioSync(guard) => guard.deref_mut(),
767 #[allow(unreachable_patterns)]
768 _ => unreachable!(),
769 }
770 }
771
772 #[cfg(any(
773 feature = "wasm-sync",
774 all(
775 target_arch = "wasm32",
776 not(feature = "std-sync"),
777 not(feature = "tokio-sync")
778 )
779 ))]
780 {
781 match self {
782 SharedWriteGuard::Single(borrow) => borrow.deref_mut(),
783 #[allow(unreachable_patterns)]
784 _ => unreachable!(),
785 }
786 }
787 }
788}
789
790#[cfg(test)]
791mod tests {
792
793 #[derive(Debug, Clone, PartialEq)]
794 #[allow(dead_code)]
795 struct TestStruct {
796 value: i32,
797 }
798
799 #[cfg(not(feature = "tokio-sync"))]
801 mod sync_tests {
802 use super::*;
803 use crate::SharedContainer;
804
805 #[test]
806 fn test_read_access() {
807 let container = SharedContainer::new(TestStruct { value: 42 });
808
809 let guard = container.read().unwrap();
811 assert_eq!(guard.value, 42);
812 }
813
814 #[test]
815 fn test_write_access() {
816 let container = SharedContainer::new(TestStruct { value: 42 });
817
818 {
820 let mut guard = container.write().unwrap();
821 guard.value = 100;
822 }
823
824 let guard = container.read().unwrap();
826 assert_eq!(guard.value, 100);
827 }
828
829 #[test]
830 fn test_clone_container() {
831 let container1 = SharedContainer::new(TestStruct { value: 42 });
832 let container2 = container1.clone();
833
834 {
836 let mut guard = container2.write().unwrap();
837 guard.value = 100;
838 }
839
840 let guard = container1.read().unwrap();
842 assert_eq!(guard.value, 100);
843 }
844
845 #[test]
846 fn test_get_cloned() {
847 let container = SharedContainer::new(TestStruct { value: 42 });
848 let cloned = container.get_cloned().unwrap();
849 assert_eq!(cloned, TestStruct { value: 42 });
850
851 {
853 let mut guard = container.write().unwrap();
854 guard.value = 100;
855 }
856
857 assert_eq!(cloned, TestStruct { value: 42 });
859
860 let new_clone = container.get_cloned().unwrap();
862 assert_eq!(new_clone, TestStruct { value: 100 });
863 }
864
865 #[test]
866 fn test_weak_ref() {
867 let container = SharedContainer::new(TestStruct { value: 42 });
868
869 let weak = container.downgrade();
871
872 let container2 = weak.upgrade().unwrap();
874
875 {
877 let mut guard = container2.write().unwrap();
878 guard.value = 100;
879 }
880
881 {
883 let guard = container.read().unwrap();
884 assert_eq!(guard.value, 100);
885 }
886 drop(container);
888 drop(container2);
889
890 assert!(weak.upgrade().is_none());
892 }
893
894 #[test]
895 fn test_weak_clone() {
896 let container = SharedContainer::new(TestStruct { value: 42 });
897
898 let weak1 = container.downgrade();
900 let weak2 = weak1.clone();
901
902 let container1 = weak1.upgrade().unwrap();
904 let container2 = weak2.upgrade().unwrap();
905
906 {
908 let mut guard = container2.write().unwrap();
909 guard.value = 100;
910 }
911
912 {
914 let guard = container1.read().unwrap();
915 assert_eq!(guard.value, 100);
916 }
917
918 drop(container);
920 drop(container1);
921 drop(container2);
922
923 assert!(weak1.upgrade().is_none());
925 assert!(weak2.upgrade().is_none());
926 }
927 }
928}
929
930#[cfg(test)]
932#[cfg(feature = "tokio-sync")]
933mod tokio_tests {
934 use super::*;
935 use tokio::runtime::Runtime;
936
937 #[derive(Debug, Clone, PartialEq)]
938 struct TestStruct {
939 value: i32,
940 }
941
942 #[test]
943 fn test_tokio_read_access() {
944 let rt = Runtime::new().unwrap();
945 rt.block_on(async {
946 let container = SharedContainer::new(TestStruct { value: 42 });
947
948 assert!(container.read().is_none());
950
951 let guard = container.read_async().await;
953 assert_eq!(guard.value, 42);
954 });
955 }
956
957 #[test]
958 fn test_tokio_write_access() {
959 let rt = Runtime::new().unwrap();
960 rt.block_on(async {
961 let container = SharedContainer::new(TestStruct { value: 42 });
962
963 assert!(container.write().is_none());
965
966 {
968 let mut guard = container.write_async().await;
969 guard.value = 100;
970 }
971
972 let guard = container.read_async().await;
974 assert_eq!(guard.value, 100);
975 });
976 }
977
978 #[test]
979 fn test_tokio_clone_container() {
980 let rt = Runtime::new().unwrap();
981 rt.block_on(async {
982 let container1 = SharedContainer::new(TestStruct { value: 42 });
983 let container2 = container1.clone();
984
985 {
987 let mut guard = container2.write_async().await;
988 guard.value = 100;
989 }
990
991 let guard = container1.read_async().await;
993 assert_eq!(guard.value, 100);
994 });
995 }
996
997 #[test]
998 fn test_tokio_weak_ref() {
999 let rt = Runtime::new().unwrap();
1000 rt.block_on(async {
1001 let container = SharedContainer::new(TestStruct { value: 42 });
1002
1003 let weak = container.downgrade();
1005
1006 let container2 = weak.upgrade().unwrap();
1008
1009 {
1011 let mut guard = container2.write_async().await;
1012 guard.value = 100;
1013 }
1014
1015 {
1017 let guard = container.read_async().await;
1018 assert_eq!(guard.value, 100);
1019 }
1020
1021 drop(container);
1023 drop(container2);
1024
1025 assert!(weak.upgrade().is_none());
1027 });
1028 }
1029}
1030
1031#[cfg(test)]
1034#[cfg(any(target_arch = "wasm32", feature = "force-wasm-impl"))]
1035mod wasm_tests {
1036 use super::*;
1037
1038 #[derive(Debug, Clone, PartialEq)]
1039 struct TestStruct {
1040 value: i32,
1041 }
1042
1043 #[test]
1044 fn test_wasm_read_access() {
1045 let container = SharedContainer::new(TestStruct { value: 42 });
1046
1047 let guard = container.read().unwrap();
1049 assert_eq!(guard.value, 42);
1050 }
1051
1052 #[test]
1053 fn test_wasm_write_access() {
1054 let container = SharedContainer::new(TestStruct { value: 42 });
1055
1056 {
1058 let mut guard = container.write().unwrap();
1059 guard.value = 100;
1060 }
1061
1062 let guard = container.read().unwrap();
1064 assert_eq!(guard.value, 100);
1065 }
1066
1067 #[test]
1068 fn test_wasm_borrow_conflict() {
1069 let container = SharedContainer::new(TestStruct { value: 42 });
1070
1071 let _guard = container.read().unwrap();
1073
1074 assert!(container.write().is_none());
1076 }
1077
1078 #[test]
1079 fn test_wasm_multiple_reads() {
1080 let container = SharedContainer::new(TestStruct { value: 42 });
1081
1082 let _guard1 = container.read().unwrap();
1084 let guard2 = container.read().unwrap();
1085
1086 assert_eq!(guard2.value, 42);
1087 }
1088
1089 #[test]
1090 fn test_wasm_weak_ref() {
1091 let container = SharedContainer::new(TestStruct { value: 42 });
1092 let weak = container.downgrade();
1093
1094 let container2 = weak.upgrade().unwrap();
1096 assert_eq!(container2.read().unwrap().value, 42);
1097
1098 drop(container);
1100 drop(container2);
1101 assert!(weak.upgrade().is_none());
1102 }
1103}