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 #[cfg_attr(feature = "tokio-sync", deprecated(
281 since = "0.2.5",
282 note = "This method uses blocking operations when using tokio-sync feature, which is not ideal for async code. Consider using get_cloned_async() instead."
283 ))]
284 pub fn get_cloned(&self) -> Option<T> {
285 #[cfg(feature = "tokio-sync")]
286 {
287 use std::sync::Arc;
291 let inner = Arc::clone(&self.tokio_inner);
292 let value = tokio::task::block_in_place(|| {
293 let rt = tokio::runtime::Builder::new_current_thread()
295 .enable_all()
296 .build()
297 .unwrap();
298 rt.block_on(async {
299 let guard = inner.read().await;
300 (*guard).clone()
301 })
302 });
303 Some(value)
304 }
305
306 #[cfg(not(feature = "tokio-sync"))]
307 {
308 let guard = self.read()?;
309 Some((*guard).clone())
310 }
311 }
312
313 #[cfg(feature = "tokio-sync")]
332 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
333 pub async fn get_cloned_async(&self) -> T
334 where
335 T: Clone,
336 {
337 let guard = self.tokio_inner.read().await;
338 (*guard).clone()
339 }
340}
341
342impl<T> SharedContainer<T> {
343 pub fn new(value: T) -> Self {
351 #[cfg(all(
352 feature = "std-sync",
353 not(feature = "tokio-sync"),
354 not(feature = "wasm-sync")
355 ))]
356 {
357 SharedContainer {
358 std_inner: Arc::new(RwLock::new(value)),
359 }
360 }
361
362 #[cfg(feature = "tokio-sync")]
363 {
364 SharedContainer {
365 tokio_inner: Arc::new(RwLock::new(value)),
366 }
367 }
368
369 #[cfg(any(
370 feature = "wasm-sync",
371 all(
372 target_arch = "wasm32",
373 not(feature = "std-sync"),
374 not(feature = "tokio-sync")
375 )
376 ))]
377 {
378 SharedContainer {
379 wasm_inner: Rc::new(RefCell::new(value)),
380 }
381 }
382 }
383
384 #[cfg_attr(feature = "tokio-sync", deprecated(
394 since = "0.2.5",
395 note = "This method always returns None when using tokio-sync feature. Use read_async() instead."
396 ))]
397 pub fn read(&self) -> Option<SharedReadGuard<T>> {
398 #[cfg(all(
399 feature = "std-sync",
400 not(feature = "tokio-sync"),
401 not(feature = "wasm-sync")
402 ))]
403 {
404 match self.std_inner.read() {
405 Ok(guard) => Some(SharedReadGuard::StdSync(guard)),
406 Err(_) => None,
407 }
408 }
409
410 #[cfg(feature = "tokio-sync")]
411 {
412 None
415 }
416
417 #[cfg(any(
418 feature = "wasm-sync",
419 all(
420 target_arch = "wasm32",
421 not(feature = "std-sync"),
422 not(feature = "tokio-sync")
423 )
424 ))]
425 {
426 match self.wasm_inner.try_borrow() {
427 Ok(borrow) => Some(SharedReadGuard::Single(borrow)),
428 Err(_) => None,
429 }
430 }
431 }
432
433 #[cfg_attr(feature = "tokio-sync", deprecated(
443 since = "0.2.5",
444 note = "This method always returns None when using tokio-sync feature. Use write_async() instead."
445 ))]
446 pub fn write(&self) -> Option<SharedWriteGuard<T>> {
447 #[cfg(all(
448 feature = "std-sync",
449 not(feature = "tokio-sync"),
450 not(feature = "wasm-sync")
451 ))]
452 {
453 match self.std_inner.write() {
454 Ok(guard) => Some(SharedWriteGuard::StdSync(guard)),
455 Err(_) => None,
456 }
457 }
458
459 #[cfg(feature = "tokio-sync")]
460 {
461 None
464 }
465
466 #[cfg(any(
467 feature = "wasm-sync",
468 all(
469 target_arch = "wasm32",
470 not(feature = "std-sync"),
471 not(feature = "tokio-sync")
472 )
473 ))]
474 {
475 match self.wasm_inner.try_borrow_mut() {
476 Ok(borrow) => Some(SharedWriteGuard::Single(borrow)),
477 Err(_) => None,
478 }
479 }
480 }
481
482 pub fn downgrade(&self) -> WeakSharedContainer<T> {
490 #[cfg(all(
491 feature = "std-sync",
492 not(feature = "tokio-sync"),
493 not(feature = "wasm-sync")
494 ))]
495 {
496 WeakSharedContainer {
497 std_inner: Arc::downgrade(&self.std_inner),
498 }
499 }
500
501 #[cfg(feature = "tokio-sync")]
502 {
503 WeakSharedContainer {
504 tokio_inner: Arc::downgrade(&self.tokio_inner),
505 }
506 }
507
508 #[cfg(any(
509 feature = "wasm-sync",
510 all(
511 target_arch = "wasm32",
512 not(feature = "std-sync"),
513 not(feature = "tokio-sync")
514 )
515 ))]
516 {
517 WeakSharedContainer {
518 wasm_inner: Rc::downgrade(&self.wasm_inner),
519 }
520 }
521 }
522
523 #[cfg(feature = "tokio-sync")]
542 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
543 pub async fn read_async(&self) -> SharedReadGuard<'_, T> {
544 let guard = self.tokio_inner.read().await;
545 SharedReadGuard::TokioSync(guard)
546 }
547
548 #[cfg(feature = "tokio-sync")]
567 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
568 pub async fn write_async(&self) -> SharedWriteGuard<'_, T> {
569 let guard = self.tokio_inner.write().await;
570 SharedWriteGuard::TokioSync(guard)
571 }
572}
573
574impl<T> WeakSharedContainer<T> {
575 pub fn upgrade(&self) -> Option<SharedContainer<T>> {
584 #[cfg(all(
586 feature = "std-sync",
587 not(feature = "tokio-sync"),
588 not(feature = "wasm-sync")
589 ))]
590 {
591 self.std_inner.upgrade().map(|inner| SharedContainer { std_inner: inner })
592 }
593
594 #[cfg(feature = "tokio-sync")]
595 {
596 self.tokio_inner.upgrade().map(|inner| SharedContainer { tokio_inner: inner })
597 }
598
599 #[cfg(any(
600 feature = "wasm-sync",
601 all(
602 target_arch = "wasm32",
603 not(feature = "std-sync"),
604 not(feature = "tokio-sync")
605 )
606 ))]
607 {
608 self.wasm_inner.upgrade().map(|inner| SharedContainer { wasm_inner: inner })
609 }
610 }
611}
612pub enum SharedReadGuard<'a, T> {
621 #[cfg(all(
622 feature = "std-sync",
623 not(feature = "tokio-sync"),
624 not(feature = "wasm-sync")
625 ))]
626 StdSync(std::sync::RwLockReadGuard<'a, T>),
627
628 #[cfg(feature = "tokio-sync")]
629 TokioSync(tokio::sync::RwLockReadGuard<'a, T>),
630
631 #[cfg(any(
632 feature = "wasm-sync",
633 all(
634 target_arch = "wasm32",
635 not(feature = "std-sync"),
636 not(feature = "tokio-sync")
637 )
638 ))]
639 Single(Ref<'a, T>),
640}
641
642impl<'a, T> Deref for SharedReadGuard<'a, T> {
643 type Target = T;
644
645 fn deref(&self) -> &Self::Target {
646 #[cfg(all(
647 feature = "std-sync",
648 not(feature = "tokio-sync"),
649 not(feature = "wasm-sync")
650 ))]
651 {
652 match self {
653 SharedReadGuard::StdSync(guard) => guard.deref(),
654 #[allow(unreachable_patterns)]
655 _ => unreachable!(),
656 }
657 }
658
659 #[cfg(feature = "tokio-sync")]
660 {
661 match self {
662 SharedReadGuard::TokioSync(guard) => guard.deref(),
663 #[allow(unreachable_patterns)]
664 _ => unreachable!(),
665 }
666 }
667
668 #[cfg(any(
669 feature = "wasm-sync",
670 all(
671 target_arch = "wasm32",
672 not(feature = "std-sync"),
673 not(feature = "tokio-sync")
674 )
675 ))]
676 {
677 match self {
678 SharedReadGuard::Single(borrow) => borrow.deref(),
679 #[allow(unreachable_patterns)]
680 _ => unreachable!(),
681 }
682 }
683 }
684}
685
686pub enum SharedWriteGuard<'a, T> {
695 #[cfg(all(
696 feature = "std-sync",
697 not(feature = "tokio-sync"),
698 not(feature = "wasm-sync")
699 ))]
700 StdSync(std::sync::RwLockWriteGuard<'a, T>),
701
702 #[cfg(feature = "tokio-sync")]
703 TokioSync(tokio::sync::RwLockWriteGuard<'a, T>),
704
705 #[cfg(any(
706 feature = "wasm-sync",
707 all(
708 target_arch = "wasm32",
709 not(feature = "std-sync"),
710 not(feature = "tokio-sync")
711 )
712 ))]
713 Single(RefMut<'a, T>),
714}
715
716impl<'a, T> Deref for SharedWriteGuard<'a, T> {
717 type Target = T;
718
719 fn deref(&self) -> &Self::Target {
720 #[cfg(all(
721 feature = "std-sync",
722 not(feature = "tokio-sync"),
723 not(feature = "wasm-sync")
724 ))]
725 {
726 match self {
727 SharedWriteGuard::StdSync(guard) => guard.deref(),
728 #[allow(unreachable_patterns)]
729 _ => unreachable!(),
730 }
731 }
732
733 #[cfg(feature = "tokio-sync")]
734 {
735 match self {
736 SharedWriteGuard::TokioSync(guard) => guard.deref(),
737 #[allow(unreachable_patterns)]
738 _ => unreachable!(),
739 }
740 }
741
742 #[cfg(any(
743 feature = "wasm-sync",
744 all(
745 target_arch = "wasm32",
746 not(feature = "std-sync"),
747 not(feature = "tokio-sync")
748 )
749 ))]
750 {
751 match self {
752 SharedWriteGuard::Single(borrow) => borrow.deref(),
753 #[allow(unreachable_patterns)]
754 _ => unreachable!(),
755 }
756 }
757 }
758}
759
760impl<'a, T> DerefMut for SharedWriteGuard<'a, T> {
761 fn deref_mut(&mut self) -> &mut Self::Target {
762 #[cfg(all(
763 feature = "std-sync",
764 not(feature = "tokio-sync"),
765 not(feature = "wasm-sync")
766 ))]
767 {
768 match self {
769 SharedWriteGuard::StdSync(guard) => guard.deref_mut(),
770 #[allow(unreachable_patterns)]
771 _ => unreachable!(),
772 }
773 }
774
775 #[cfg(feature = "tokio-sync")]
776 {
777 match self {
778 SharedWriteGuard::TokioSync(guard) => guard.deref_mut(),
779 #[allow(unreachable_patterns)]
780 _ => unreachable!(),
781 }
782 }
783
784 #[cfg(any(
785 feature = "wasm-sync",
786 all(
787 target_arch = "wasm32",
788 not(feature = "std-sync"),
789 not(feature = "tokio-sync")
790 )
791 ))]
792 {
793 match self {
794 SharedWriteGuard::Single(borrow) => borrow.deref_mut(),
795 #[allow(unreachable_patterns)]
796 _ => unreachable!(),
797 }
798 }
799 }
800}
801
802#[cfg(test)]
803mod tests {
804
805 #[derive(Debug, Clone, PartialEq)]
806 #[allow(dead_code)]
807 struct TestStruct {
808 value: i32,
809 }
810
811 #[cfg(not(feature = "tokio-sync"))]
813 mod sync_tests {
814 use super::*;
815 use crate::SharedContainer;
816
817 #[test]
818 fn test_read_access() {
819 let container = SharedContainer::new(TestStruct { value: 42 });
820
821 let guard = container.read().unwrap();
823 assert_eq!(guard.value, 42);
824 }
825
826 #[test]
827 fn test_write_access() {
828 let container = SharedContainer::new(TestStruct { value: 42 });
829
830 {
832 let mut guard = container.write().unwrap();
833 guard.value = 100;
834 }
835
836 let guard = container.read().unwrap();
838 assert_eq!(guard.value, 100);
839 }
840
841 #[test]
842 fn test_clone_container() {
843 let container1 = SharedContainer::new(TestStruct { value: 42 });
844 let container2 = container1.clone();
845
846 {
848 let mut guard = container2.write().unwrap();
849 guard.value = 100;
850 }
851
852 let guard = container1.read().unwrap();
854 assert_eq!(guard.value, 100);
855 }
856
857 #[test]
858 fn test_get_cloned() {
859 let container = SharedContainer::new(TestStruct { value: 42 });
860 let cloned = container.get_cloned().unwrap();
861 assert_eq!(cloned, TestStruct { value: 42 });
862
863 {
865 let mut guard = container.write().unwrap();
866 guard.value = 100;
867 }
868
869 assert_eq!(cloned, TestStruct { value: 42 });
871
872 let new_clone = container.get_cloned().unwrap();
874 assert_eq!(new_clone, TestStruct { value: 100 });
875 }
876
877 #[test]
878 fn test_weak_ref() {
879 let container = SharedContainer::new(TestStruct { value: 42 });
880
881 let weak = container.downgrade();
883
884 let container2 = weak.upgrade().unwrap();
886
887 {
889 let mut guard = container2.write().unwrap();
890 guard.value = 100;
891 }
892
893 {
895 let guard = container.read().unwrap();
896 assert_eq!(guard.value, 100);
897 }
898 drop(container);
900 drop(container2);
901
902 assert!(weak.upgrade().is_none());
904 }
905
906 #[test]
907 fn test_weak_clone() {
908 let container = SharedContainer::new(TestStruct { value: 42 });
909
910 let weak1 = container.downgrade();
912 let weak2 = weak1.clone();
913
914 let container1 = weak1.upgrade().unwrap();
916 let container2 = weak2.upgrade().unwrap();
917
918 {
920 let mut guard = container2.write().unwrap();
921 guard.value = 100;
922 }
923
924 {
926 let guard = container1.read().unwrap();
927 assert_eq!(guard.value, 100);
928 }
929
930 drop(container);
932 drop(container1);
933 drop(container2);
934
935 assert!(weak1.upgrade().is_none());
937 assert!(weak2.upgrade().is_none());
938 }
939 }
940}
941
942#[cfg(test)]
944#[cfg(feature = "tokio-sync")]
945mod tokio_tests {
946 use super::*;
947 use tokio::runtime::Runtime;
948
949 #[derive(Debug, Clone, PartialEq)]
950 struct TestStruct {
951 value: i32,
952 }
953
954 #[test]
955 fn test_tokio_read_access() {
956 let rt = Runtime::new().unwrap();
957 rt.block_on(async {
958 let container = SharedContainer::new(TestStruct { value: 42 });
959
960 assert!(container.read().is_none());
962
963 let guard = container.read_async().await;
965 assert_eq!(guard.value, 42);
966 });
967 }
968
969 #[test]
970 fn test_tokio_write_access() {
971 let rt = Runtime::new().unwrap();
972 rt.block_on(async {
973 let container = SharedContainer::new(TestStruct { value: 42 });
974
975 assert!(container.write().is_none());
977
978 {
980 let mut guard = container.write_async().await;
981 guard.value = 100;
982 }
983
984 let guard = container.read_async().await;
986 assert_eq!(guard.value, 100);
987 });
988 }
989
990 #[test]
991 fn test_tokio_clone_container() {
992 let rt = Runtime::new().unwrap();
993 rt.block_on(async {
994 let container1 = SharedContainer::new(TestStruct { value: 42 });
995 let container2 = container1.clone();
996
997 {
999 let mut guard = container2.write_async().await;
1000 guard.value = 100;
1001 }
1002
1003 let guard = container1.read_async().await;
1005 assert_eq!(guard.value, 100);
1006 });
1007 }
1008
1009 #[test]
1010 fn test_tokio_weak_ref() {
1011 let rt = Runtime::new().unwrap();
1012 rt.block_on(async {
1013 let container = SharedContainer::new(TestStruct { value: 42 });
1014
1015 let weak = container.downgrade();
1017
1018 let container2 = weak.upgrade().unwrap();
1020
1021 {
1023 let mut guard = container2.write_async().await;
1024 guard.value = 100;
1025 }
1026
1027 {
1029 let guard = container.read_async().await;
1030 assert_eq!(guard.value, 100);
1031 }
1032
1033 drop(container);
1035 drop(container2);
1036
1037 assert!(weak.upgrade().is_none());
1039 });
1040 }
1041}
1042
1043#[cfg(test)]
1046#[cfg(any(target_arch = "wasm32", feature = "force-wasm-impl"))]
1047mod wasm_tests {
1048 use super::*;
1049
1050 #[derive(Debug, Clone, PartialEq)]
1051 struct TestStruct {
1052 value: i32,
1053 }
1054
1055 #[test]
1056 fn test_wasm_read_access() {
1057 let container = SharedContainer::new(TestStruct { value: 42 });
1058
1059 let guard = container.read().unwrap();
1061 assert_eq!(guard.value, 42);
1062 }
1063
1064 #[test]
1065 fn test_wasm_write_access() {
1066 let container = SharedContainer::new(TestStruct { value: 42 });
1067
1068 {
1070 let mut guard = container.write().unwrap();
1071 guard.value = 100;
1072 }
1073
1074 let guard = container.read().unwrap();
1076 assert_eq!(guard.value, 100);
1077 }
1078
1079 #[test]
1080 fn test_wasm_borrow_conflict() {
1081 let container = SharedContainer::new(TestStruct { value: 42 });
1082
1083 let _guard = container.read().unwrap();
1085
1086 assert!(container.write().is_none());
1088 }
1089
1090 #[test]
1091 fn test_wasm_multiple_reads() {
1092 let container = SharedContainer::new(TestStruct { value: 42 });
1093
1094 let _guard1 = container.read().unwrap();
1096 let guard2 = container.read().unwrap();
1097
1098 assert_eq!(guard2.value, 42);
1099 }
1100
1101 #[test]
1102 fn test_wasm_weak_ref() {
1103 let container = SharedContainer::new(TestStruct { value: 42 });
1104 let weak = container.downgrade();
1105
1106 let container2 = weak.upgrade().unwrap();
1108 assert_eq!(container2.read().unwrap().value, 42);
1109
1110 drop(container);
1112 drop(container2);
1113 assert!(weak.upgrade().is_none());
1114 }
1115}