shared_container/
lib.rs

1//! # Shared Container Module
2//!
3//! This module provides a unified abstraction over different container types
4//! used for shared data access with interior mutability in different contexts.
5//!
6//! It abstracts over the differences between:
7//! - `Arc<std::sync::RwLock<T>>` (used in standard multi-threaded environments)
8//! - `Arc<tokio::sync::RwLock<T>>` (used for async/await support)
9//! - `Rc<RefCell<T>>` (used in single-threaded environments like WebAssembly)
10//!
11//! This allows code using these containers to be written once but work efficiently
12//! in different contexts.
13//!
14//! ## Feature Flags
15//!
16//! This library provides several feature flags to customize its behavior:
17//!
18//! - **std-sync** (default): Uses `Arc<std::sync::RwLock<T>>` for thread-safe access
19//! - **tokio-sync**: Uses `Arc<tokio::sync::RwLock<T>>` for async/await support
20//! - **wasm-sync**: Uses `Rc<RefCell<T>>` for single-threaded environments
21//! - **force-wasm-impl**: Legacy feature, equivalent to wasm-sync
22//!
23//! ## Async Support
24//!
25//! When the `tokio-sync` feature is enabled, the library provides async versions of the read and write methods:
26//!
27//! ```rust
28//! # #[cfg(feature = "tokio-sync")]
29//! # async fn example() {
30//! # use shared_container::SharedContainer;
31//! let container = SharedContainer::new(42);
32//!
33//! // Read access
34//! let guard = container.read_async().await;
35//! println!("Value: {}", *guard);
36//!
37//! // Write access
38//! let mut guard = container.write_async().await;
39//! *guard = 100;
40//! # }
41//! ```
42//!
43//! Note that when using the `tokio-sync` feature, the synchronous `read()` and `write()` methods
44//! will always return `None`. You should use the async methods instead.
45
46use std::fmt::Debug;
47use std::ops::{Deref, DerefMut};
48
49// Standard library synchronization primitives (default)
50#[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
51use std::sync::{Arc, RwLock, Weak};
52
53// Tokio async synchronization primitives
54#[cfg(feature = "tokio-sync")]
55use std::sync::{Arc, Weak};
56#[cfg(feature = "tokio-sync")]
57use tokio::sync::RwLock;
58
59// WebAssembly/single-threaded synchronization primitives
60#[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
61use std::cell::{Ref, RefCell, RefMut};
62#[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
63use std::rc::{Rc, Weak};
64
65
66/// A unified container for shared data that works in both multi-threaded and single-threaded environments.
67///
68/// This struct provides an abstraction over different container types:
69/// - `Arc<std::sync::RwLock<T>>` (used in standard multi-threaded environments)
70/// - `Arc<tokio::sync::RwLock<T>>` (used for async/await support)
71/// - `Rc<RefCell<T>>` (used in single-threaded environments like WebAssembly)
72///
73/// It allows code to be written once but compile to the most efficient implementation
74/// based on the environment where it will run and the features enabled.
75#[derive(Debug)]
76pub struct SharedContainer<T: Debug> {
77    // Standard library thread-safe implementation
78    #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
79    inner: Arc<RwLock<T>>,
80
81    // Tokio async implementation
82    #[cfg(feature = "tokio-sync")]
83    inner: Arc<RwLock<T>>,
84
85    // Single-threaded implementation for WebAssembly
86    #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
87    inner: Rc<RefCell<T>>,
88}
89
90// Implement Send and Sync for SharedContainer only for thread-safe implementations
91#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
92unsafe impl<T: Debug + Send> Send for SharedContainer<T> {}
93
94#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
95unsafe impl<T: Debug + Send + Sync> Sync for SharedContainer<T> {}
96
97/// A weak reference to a `SharedContainer`.
98///
99/// This struct provides an abstraction over different weak reference types:
100/// - `Weak<std::sync::RwLock<T>>` (used in standard multi-threaded environments)
101/// - `Weak<tokio::sync::RwLock<T>>` (used for async/await support)
102/// - `Weak<RefCell<T>>` (used in single-threaded environments like WebAssembly)
103///
104/// Weak references don't prevent the value from being dropped when no strong references
105/// remain. This helps break reference cycles that could cause memory leaks.
106#[derive(Debug)]
107pub struct WeakSharedContainer<T: Debug> {
108    // Standard library thread-safe implementation
109    #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
110    inner: Weak<RwLock<T>>,
111
112    // Tokio async implementation
113    #[cfg(feature = "tokio-sync")]
114    inner: Weak<RwLock<T>>,
115
116    // Single-threaded implementation for WebAssembly
117    #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
118    inner: Weak<RefCell<T>>,
119}
120
121impl<T: Debug> Clone for WeakSharedContainer<T> {
122    fn clone(&self) -> Self {
123        // Same implementation for both platforms, but different underlying types
124        WeakSharedContainer {
125            inner: self.inner.clone(),
126        }
127    }
128}
129
130impl<T: Debug + PartialEq> PartialEq for SharedContainer<T> {
131    fn eq(&self, other: &Self) -> bool {
132        #[cfg(feature = "tokio-sync")]
133        {
134            // For tokio-sync, we need to block on the async read
135            use std::sync::Arc;
136            let self_inner = Arc::clone(&self.inner);
137            let other_inner = Arc::clone(&other.inner);
138
139            tokio::task::block_in_place(|| {
140                tokio::runtime::Handle::current().block_on(async {
141                    let self_val = self_inner.read().await;
142                    let other_val = other_inner.read().await;
143                    *self_val == *other_val
144                })
145            })
146        }
147
148        #[cfg(not(feature = "tokio-sync"))]
149        {
150            match (self.read(), other.read()) {
151                (Some(self_val), Some(other_val)) => *self_val == *other_val,
152                _ => false,
153            }
154        }
155    }
156}
157
158impl<T: Debug> Clone for SharedContainer<T> {
159    fn clone(&self) -> Self {
160        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
161        {
162            SharedContainer {
163                inner: Arc::clone(&self.inner),
164            }
165        }
166
167        #[cfg(feature = "tokio-sync")]
168        {
169            SharedContainer {
170                inner: Arc::clone(&self.inner),
171            }
172        }
173
174        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
175        {
176            SharedContainer {
177                inner: Rc::clone(&self.inner),
178            }
179        }
180    }
181}
182
183impl<T: Debug + Clone> SharedContainer<T> {
184    /// Gets a clone of the contained value.
185    ///
186    /// This method acquires a read lock, clones the value, and releases the lock.
187    ///
188    /// # Returns
189    /// * `Some(T)`: A clone of the contained value
190    /// * `None`: If the lock couldn't be acquired
191    ///
192    /// # Note
193    /// When using the `tokio-sync` feature, this method will try to acquire the lock
194    /// in a blocking manner, which may not be ideal for async code. Consider using
195    /// `get_cloned_async()` instead.
196    pub fn get_cloned(&self) -> Option<T> {
197        #[cfg(feature = "tokio-sync")]
198        {
199            // For tokio-sync, we need to block on the async read
200            // This is not ideal for async code, but it allows the method to work
201            // in both sync and async contexts
202            use std::sync::Arc;
203            let inner = Arc::clone(&self.inner);
204            let value = tokio::task::block_in_place(|| {
205                tokio::runtime::Handle::current().block_on(async {
206                    let guard = inner.read().await;
207                    (*guard).clone()
208                })
209            });
210            Some(value)
211        }
212
213        #[cfg(not(feature = "tokio-sync"))]
214        {
215            let guard = self.read()?;
216            Some((*guard).clone())
217        }
218    }
219
220    /// Gets a clone of the contained value asynchronously.
221    ///
222    /// This method is only available when the `tokio-sync` feature is enabled.
223    ///
224    /// # Returns
225    /// A clone of the contained value
226    ///
227    /// # Examples
228    ///
229    /// ```
230    /// # #[cfg(feature = "tokio-sync")]
231    /// # async fn example() {
232    /// # use shared_container::SharedContainer;
233    /// let container = SharedContainer::new(42);
234    /// let value = container.get_cloned_async().await;
235    /// assert_eq!(value, 42);
236    /// # }
237    /// ```
238    #[cfg(feature = "tokio-sync")]
239    pub async fn get_cloned_async(&self) -> T
240    where
241        T: Clone,
242    {
243        let guard = self.inner.read().await;
244        (*guard).clone()
245    }
246}
247
248impl<T: Debug> SharedContainer<T> {
249    /// Creates a new `SharedContainer` containing the given value.
250    ///
251    /// # Parameters
252    /// * `value`: The value to store in the container
253    ///
254    /// # Returns
255    /// A new `SharedContainer` instance containing the value
256    pub fn new(value: T) -> Self {
257        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
258        {
259            SharedContainer {
260                inner: Arc::new(RwLock::new(value)),
261            }
262        }
263
264        #[cfg(feature = "tokio-sync")]
265        {
266            SharedContainer {
267                inner: Arc::new(RwLock::new(value)),
268            }
269        }
270
271        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
272        {
273            SharedContainer {
274                inner: Rc::new(RefCell::new(value)),
275            }
276        }
277    }
278
279    /// Gets a read-only access guard to the contained value.
280    ///
281    /// # Returns
282    /// * `Some(SharedReadGuard<T>)`: A guard allowing read-only access to the value
283    /// * `None`: If the lock couldn't be acquired
284    ///
285    /// # Note
286    /// When using the `tokio-sync` feature, this method will always return `None`.
287    /// Use `read_async()` instead for async access.
288    pub fn read(&self) -> Option<SharedReadGuard<T>> {
289        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
290        {
291            match self.inner.read() {
292                Ok(guard) => Some(SharedReadGuard::StdSync(guard)),
293                Err(_) => None,
294            }
295        }
296
297        #[cfg(feature = "tokio-sync")]
298        {
299            // Tokio's RwLock doesn't have a non-async read method, so we can't use it here
300            // Users should use read_async instead
301            None
302        }
303
304        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
305        {
306            match self.inner.try_borrow() {
307                Ok(borrow) => Some(SharedReadGuard::Single(borrow)),
308                Err(_) => None,
309            }
310        }
311    }
312
313    /// Gets a writable access guard to the contained value.
314    ///
315    /// # Returns
316    /// * `Some(SharedWriteGuard<T>)`: A guard allowing read-write access to the value
317    /// * `None`: If the lock couldn't be acquired
318    ///
319    /// # Note
320    /// When using the `tokio-sync` feature, this method will always return `None`.
321    /// Use `write_async()` instead for async access.
322    pub fn write(&self) -> Option<SharedWriteGuard<T>> {
323        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
324        {
325            match self.inner.write() {
326                Ok(guard) => Some(SharedWriteGuard::StdSync(guard)),
327                Err(_) => None,
328            }
329        }
330
331        #[cfg(feature = "tokio-sync")]
332        {
333            // Tokio's RwLock doesn't have a non-async write method, so we can't use it here
334            // Users should use write_async instead
335            None
336        }
337
338        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
339        {
340            match self.inner.try_borrow_mut() {
341                Ok(borrow) => Some(SharedWriteGuard::Single(borrow)),
342                Err(_) => None,
343            }
344        }
345    }
346
347    /// Creates a weak reference to this container.
348    ///
349    /// A weak reference doesn't prevent the value from being dropped when no strong
350    /// references remain, which helps break reference cycles that could cause memory leaks.
351    ///
352    /// # Returns
353    /// A `WeakSharedContainer` that points to the same data
354    pub fn downgrade(&self) -> WeakSharedContainer<T> {
355        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
356        {
357            WeakSharedContainer {
358                inner: Arc::downgrade(&self.inner),
359            }
360        }
361
362        #[cfg(feature = "tokio-sync")]
363        {
364            WeakSharedContainer {
365                inner: Arc::downgrade(&self.inner),
366            }
367        }
368
369        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
370        {
371            WeakSharedContainer {
372                inner: Rc::downgrade(&self.inner),
373            }
374        }
375    }
376
377    /// Asynchronously gets a read-only access guard to the contained value.
378    ///
379    /// This method is only available when the `tokio-sync` feature is enabled.
380    ///
381    /// # Returns
382    /// A guard allowing read-only access to the value
383    ///
384    /// # Examples
385    ///
386    /// ```
387    /// # #[cfg(feature = "tokio-sync")]
388    /// # async fn example() {
389    /// # use shared_container::SharedContainer;
390    /// let container = SharedContainer::new(42);
391    /// let guard = container.read_async().await;
392    /// assert_eq!(*guard, 42);
393    /// # }
394    /// ```
395    #[cfg(feature = "tokio-sync")]
396    pub async fn read_async(&self) -> SharedReadGuard<'_, T> {
397        let guard = self.inner.read().await;
398        SharedReadGuard::TokioSync(guard)
399    }
400
401    /// Asynchronously gets a writable access guard to the contained value.
402    ///
403    /// This method is only available when the `tokio-sync` feature is enabled.
404    ///
405    /// # Returns
406    /// A guard allowing read-write access to the value
407    ///
408    /// # Examples
409    ///
410    /// ```
411    /// # #[cfg(feature = "tokio-sync")]
412    /// # async fn example() {
413    /// # use shared_container::SharedContainer;
414    /// let container = SharedContainer::new(42);
415    /// let mut guard = container.write_async().await;
416    /// *guard = 100;
417    /// # }
418    /// ```
419    #[cfg(feature = "tokio-sync")]
420    pub async fn write_async(&self) -> SharedWriteGuard<'_, T> {
421        let guard = self.inner.write().await;
422        SharedWriteGuard::TokioSync(guard)
423    }
424}
425
426impl<T: Debug> WeakSharedContainer<T> {
427    /// Attempts to create a strong `SharedContainer` from this weak reference.
428    ///
429    /// This will succeed if the value has not yet been dropped, i.e., if there are
430    /// still other strong references to it.
431    ///
432    /// # Returns
433    /// * `Some(SharedContainer<T>)`: If the value still exists
434    /// * `None`: If the value has been dropped
435    pub fn upgrade(&self) -> Option<SharedContainer<T>> {
436        // Code is the same for both platforms, but types are different
437        self.inner.upgrade().map(|inner| SharedContainer { inner })
438    }
439}
440/// A read-only guard for accessing data in a `SharedContainer`.
441///
442/// This type abstracts over the differences between different read guards:
443/// - `std::sync::RwLockReadGuard` (used in standard multi-threaded environments)
444/// - `tokio::sync::RwLockReadGuard` (used for async/await support)
445/// - `std::cell::Ref` (used in single-threaded environments like WebAssembly)
446///
447/// It implements `Deref` to allow transparent access to the underlying data.
448pub enum SharedReadGuard<'a, T: Debug> {
449    #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
450    StdSync(std::sync::RwLockReadGuard<'a, T>),
451
452    #[cfg(feature = "tokio-sync")]
453    TokioSync(tokio::sync::RwLockReadGuard<'a, T>),
454
455    #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
456    Single(Ref<'a, T>),
457}
458
459impl<'a, T: Debug> Deref for SharedReadGuard<'a, T> {
460    type Target = T;
461
462    fn deref(&self) -> &Self::Target {
463        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
464        {
465            match self {
466                SharedReadGuard::StdSync(guard) => guard.deref(),
467                #[allow(unreachable_patterns)]
468                _ => unreachable!(),
469            }
470        }
471
472        #[cfg(feature = "tokio-sync")]
473        {
474            match self {
475                SharedReadGuard::TokioSync(guard) => guard.deref(),
476                #[allow(unreachable_patterns)]
477                _ => unreachable!(),
478            }
479        }
480
481        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
482        {
483            match self {
484                SharedReadGuard::Single(borrow) => borrow.deref(),
485                #[allow(unreachable_patterns)]
486                _ => unreachable!(),
487            }
488        }
489    }
490}
491
492/// A writable guard for accessing and modifying data in a `SharedContainer`.
493///
494/// This type abstracts over the differences between different write guards:
495/// - `std::sync::RwLockWriteGuard` (used in standard multi-threaded environments)
496/// - `tokio::sync::RwLockWriteGuard` (used for async/await support)
497/// - `std::cell::RefMut` (used in single-threaded environments like WebAssembly)
498///
499/// It implements both `Deref` and `DerefMut` to allow transparent access to the underlying data.
500pub enum SharedWriteGuard<'a, T: Debug> {
501    #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
502    StdSync(std::sync::RwLockWriteGuard<'a, T>),
503
504    #[cfg(feature = "tokio-sync")]
505    TokioSync(tokio::sync::RwLockWriteGuard<'a, T>),
506
507    #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
508    Single(RefMut<'a, T>),
509}
510
511impl<'a, T: Debug> Deref for SharedWriteGuard<'a, T> {
512    type Target = T;
513
514    fn deref(&self) -> &Self::Target {
515        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
516        {
517            match self {
518                SharedWriteGuard::StdSync(guard) => guard.deref(),
519                #[allow(unreachable_patterns)]
520                _ => unreachable!(),
521            }
522        }
523
524        #[cfg(feature = "tokio-sync")]
525        {
526            match self {
527                SharedWriteGuard::TokioSync(guard) => guard.deref(),
528                #[allow(unreachable_patterns)]
529                _ => unreachable!(),
530            }
531        }
532
533        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
534        {
535            match self {
536                SharedWriteGuard::Single(borrow) => borrow.deref(),
537                #[allow(unreachable_patterns)]
538                _ => unreachable!(),
539            }
540        }
541    }
542}
543
544impl<'a, T: Debug> DerefMut for SharedWriteGuard<'a, T> {
545    fn deref_mut(&mut self) -> &mut Self::Target {
546        #[cfg(all(feature = "std-sync", not(feature = "tokio-sync"), not(feature = "wasm-sync")))]
547        {
548            match self {
549                SharedWriteGuard::StdSync(guard) => guard.deref_mut(),
550                #[allow(unreachable_patterns)]
551                _ => unreachable!(),
552            }
553        }
554
555        #[cfg(feature = "tokio-sync")]
556        {
557            match self {
558                SharedWriteGuard::TokioSync(guard) => guard.deref_mut(),
559                #[allow(unreachable_patterns)]
560                _ => unreachable!(),
561            }
562        }
563
564        #[cfg(any(feature = "wasm-sync", all(target_arch = "wasm32", not(feature = "std-sync"), not(feature = "tokio-sync"))))]
565        {
566            match self {
567                SharedWriteGuard::Single(borrow) => borrow.deref_mut(),
568                #[allow(unreachable_patterns)]
569                _ => unreachable!(),
570            }
571        }
572    }
573}
574
575#[cfg(test)]
576mod tests {
577    use super::*;
578
579    #[derive(Debug, Clone, PartialEq)]
580    #[allow(dead_code)]
581    struct TestStruct {
582        value: i32,
583    }
584
585    // Skip synchronous tests when tokio-sync is enabled
586    #[cfg(not(feature = "tokio-sync"))]
587    mod sync_tests {
588        use super::*;
589
590        #[test]
591        fn test_read_access() {
592            let container = SharedContainer::new(TestStruct { value: 42 });
593
594            // Read access
595            let guard = container.read().unwrap();
596            assert_eq!(guard.value, 42);
597        }
598
599        #[test]
600        fn test_write_access() {
601            let container = SharedContainer::new(TestStruct { value: 42 });
602
603            // Write access
604            {
605                let mut guard = container.write().unwrap();
606                guard.value = 100;
607            }
608
609            // Verify change
610            let guard = container.read().unwrap();
611            assert_eq!(guard.value, 100);
612        }
613
614        #[test]
615        fn test_clone_container() {
616            let container1 = SharedContainer::new(TestStruct { value: 42 });
617            let container2 = container1.clone();
618
619            // Modify through container2
620            {
621                let mut guard = container2.write().unwrap();
622                guard.value = 100;
623            }
624
625            // Verify change visible through container1
626            let guard = container1.read().unwrap();
627            assert_eq!(guard.value, 100);
628        }
629
630        #[test]
631        fn test_get_cloned() {
632            let container = SharedContainer::new(TestStruct { value: 42 });
633            let cloned = container.get_cloned().unwrap();
634            assert_eq!(cloned, TestStruct { value: 42 });
635
636            // Modify original
637            {
638                let mut guard = container.write().unwrap();
639                guard.value = 100;
640            }
641
642            // Cloned value should not change
643            assert_eq!(cloned, TestStruct { value: 42 });
644
645            // And we can get a new clone with updated value
646            let new_clone = container.get_cloned().unwrap();
647            assert_eq!(new_clone, TestStruct { value: 100 });
648        }
649
650        #[test]
651        fn test_weak_ref() {
652            let container = SharedContainer::new(TestStruct { value: 42 });
653
654            // Create a weak reference
655            let weak = container.downgrade();
656
657            // Weak reference can be upgraded to a strong reference
658            let container2 = weak.upgrade().unwrap();
659
660            // Both containers point to the same data
661            {
662                let mut guard = container2.write().unwrap();
663                guard.value = 100;
664            }
665
666            // Change visible through first container
667            {
668                let guard = container.read().unwrap();
669                assert_eq!(guard.value, 100);
670            }
671            // Drop all strong references
672            drop(container);
673            drop(container2);
674
675            // Weak reference can no longer be upgraded
676            assert!(weak.upgrade().is_none());
677        }
678
679        #[test]
680        fn test_weak_clone() {
681            let container = SharedContainer::new(TestStruct { value: 42 });
682
683            // Create a weak reference and clone it
684            let weak1 = container.downgrade();
685            let weak2 = weak1.clone();
686
687            // Both weak references can be upgraded
688            let container1 = weak1.upgrade().unwrap();
689            let container2 = weak2.upgrade().unwrap();
690
691            // Modify through container2
692            {
693                let mut guard = container2.write().unwrap();
694                guard.value = 100;
695            }
696
697            // Change visible through container1
698            {
699                let guard = container1.read().unwrap();
700                assert_eq!(guard.value, 100);
701            }
702
703            // Drop all strong references
704            drop(container);
705            drop(container1);
706            drop(container2);
707
708            // Neither weak reference can be upgraded
709            assert!(weak1.upgrade().is_none());
710            assert!(weak2.upgrade().is_none());
711        }
712    }
713}
714
715// Tests specifically for the tokio async implementation
716#[cfg(test)]
717#[cfg(feature = "tokio-sync")]
718mod tokio_tests {
719    use super::*;
720    use tokio::runtime::Runtime;
721
722    #[derive(Debug, Clone, PartialEq)]
723    struct TestStruct {
724        value: i32,
725    }
726
727    #[test]
728    fn test_tokio_read_access() {
729        let rt = Runtime::new().unwrap();
730        rt.block_on(async {
731            let container = SharedContainer::new(TestStruct { value: 42 });
732
733            // Synchronous read should return None with tokio-sync
734            assert!(container.read().is_none());
735
736            // Async read access
737            let guard = container.read_async().await;
738            assert_eq!(guard.value, 42);
739        });
740    }
741
742    #[test]
743    fn test_tokio_write_access() {
744        let rt = Runtime::new().unwrap();
745        rt.block_on(async {
746            let container = SharedContainer::new(TestStruct { value: 42 });
747
748            // Synchronous write should return None with tokio-sync
749            assert!(container.write().is_none());
750
751            // Async write access
752            {
753                let mut guard = container.write_async().await;
754                guard.value = 100;
755            }
756
757            // Verify change
758            let guard = container.read_async().await;
759            assert_eq!(guard.value, 100);
760        });
761    }
762
763    #[test]
764    fn test_tokio_clone_container() {
765        let rt = Runtime::new().unwrap();
766        rt.block_on(async {
767            let container1 = SharedContainer::new(TestStruct { value: 42 });
768            let container2 = container1.clone();
769
770            // Modify through container2
771            {
772                let mut guard = container2.write_async().await;
773                guard.value = 100;
774            }
775
776            // Verify change visible through container1
777            let guard = container1.read_async().await;
778            assert_eq!(guard.value, 100);
779        });
780    }
781
782    #[test]
783    fn test_tokio_weak_ref() {
784        let rt = Runtime::new().unwrap();
785        rt.block_on(async {
786            let container = SharedContainer::new(TestStruct { value: 42 });
787
788            // Create a weak reference
789            let weak = container.downgrade();
790
791            // Weak reference can be upgraded to a strong reference
792            let container2 = weak.upgrade().unwrap();
793
794            // Both containers point to the same data
795            {
796                let mut guard = container2.write_async().await;
797                guard.value = 100;
798            }
799
800            // Change visible through first container
801            {
802                let guard = container.read_async().await;
803                assert_eq!(guard.value, 100);
804            }
805
806            // Drop all strong references
807            drop(container);
808            drop(container2);
809
810            // Weak reference can no longer be upgraded
811            assert!(weak.upgrade().is_none());
812        });
813    }
814}
815
816// Tests specifically for the WebAssembly implementation
817// These tests can be run on any platform by enabling the force-wasm-impl feature
818#[cfg(test)]
819#[cfg(any(target_arch = "wasm32", feature = "force-wasm-impl"))]
820mod wasm_tests {
821    use super::*;
822
823    #[derive(Debug, Clone, PartialEq)]
824    struct TestStruct {
825        value: i32,
826    }
827
828    #[test]
829    fn test_wasm_read_access() {
830        let container = SharedContainer::new(TestStruct { value: 42 });
831
832        // Read access
833        let guard = container.read().unwrap();
834        assert_eq!(guard.value, 42);
835    }
836
837    #[test]
838    fn test_wasm_write_access() {
839        let container = SharedContainer::new(TestStruct { value: 42 });
840
841        // Write access
842        {
843            let mut guard = container.write().unwrap();
844            guard.value = 100;
845        }
846
847        // Verify change
848        let guard = container.read().unwrap();
849        assert_eq!(guard.value, 100);
850    }
851
852    #[test]
853    fn test_wasm_borrow_conflict() {
854        let container = SharedContainer::new(TestStruct { value: 42 });
855
856        // Get a read borrow
857        let _guard = container.read().unwrap();
858
859        // Trying to get a write borrow while a read borrow exists should fail
860        assert!(container.write().is_none());
861    }
862
863    #[test]
864    fn test_wasm_multiple_reads() {
865        let container = SharedContainer::new(TestStruct { value: 42 });
866
867        // Multiple read borrows should work
868        let _guard1 = container.read().unwrap();
869        let guard2 = container.read().unwrap();
870
871        assert_eq!(guard2.value, 42);
872    }
873
874    #[test]
875    fn test_wasm_weak_ref() {
876        let container = SharedContainer::new(TestStruct { value: 42 });
877        let weak = container.downgrade();
878
879        // Upgrade should work
880        let container2 = weak.upgrade().unwrap();
881        assert_eq!(container2.read().unwrap().value, 42);
882
883        // After dropping all strong references, upgrade should fail
884        drop(container);
885        drop(container2);
886        assert!(weak.upgrade().is_none());
887    }
888}