#![cfg_attr(docsrs, feature(doc_cfg))]
use std::ops::{Deref, DerefMut};
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
use std::sync::{Arc, RwLock, Weak};
#[cfg(feature = "tokio-sync")]
use std::sync::{Arc, Weak};
#[cfg(feature = "tokio-sync")]
use tokio::sync::RwLock;
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
use std::cell::{Ref, RefCell, RefMut};
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
use std::rc::{Rc, Weak as RcWeak};
#[deprecated(
since = "0.3.0",
note = "Use `Shared<T>` for sync or `AsyncShared<T>` for async instead. See migration guide in docs."
)]
#[derive(Debug)]
pub struct SharedContainer<T> {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
std_inner: Arc<RwLock<T>>,
#[cfg(feature = "tokio-sync")]
tokio_inner: Arc<RwLock<T>>,
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
wasm_inner: Rc<RefCell<T>>,
}
#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
unsafe impl<T: Send> Send for SharedContainer<T> {}
#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
unsafe impl<T: Send + Sync> Sync for SharedContainer<T> {}
#[deprecated(
since = "0.3.0",
note = "Use `WeakShared<T>` for sync or `WeakAsyncShared<T>` for async instead."
)]
#[derive(Debug)]
pub struct WeakSharedContainer<T> {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
std_inner: Weak<RwLock<T>>,
#[cfg(feature = "tokio-sync")]
tokio_inner: Weak<RwLock<T>>,
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
wasm_inner: RcWeak<RefCell<T>>,
}
impl<T> Clone for WeakSharedContainer<T> {
fn clone(&self) -> Self {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
WeakSharedContainer {
std_inner: self.std_inner.clone(),
}
}
#[cfg(feature = "tokio-sync")]
{
WeakSharedContainer {
tokio_inner: self.tokio_inner.clone(),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
WeakSharedContainer {
wasm_inner: self.wasm_inner.clone(),
}
}
}
}
impl<T: PartialEq> PartialEq for SharedContainer<T> {
fn eq(&self, _other: &Self) -> bool {
#[cfg(feature = "tokio-sync")]
{
false
}
#[cfg(not(feature = "tokio-sync"))]
{
match (self.read(), _other.read()) {
(Some(self_val), Some(other_val)) => *self_val == *other_val,
_ => false,
}
}
}
}
impl<T> Clone for SharedContainer<T> {
fn clone(&self) -> Self {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
SharedContainer {
std_inner: Arc::clone(&self.std_inner),
}
}
#[cfg(feature = "tokio-sync")]
{
SharedContainer {
tokio_inner: Arc::clone(&self.tokio_inner),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
SharedContainer {
wasm_inner: Rc::clone(&self.wasm_inner),
}
}
}
}
impl<T: Clone> SharedContainer<T> {
#[cfg_attr(
feature = "tokio-sync",
doc = "WARNING: This method uses blocking operations when using tokio-sync feature, which is not ideal for async code. Consider using get_cloned_async() instead."
)]
pub fn get_cloned(&self) -> Option<T> {
#[cfg(feature = "tokio-sync")]
{
None
}
#[cfg(not(feature = "tokio-sync"))]
{
let guard = self.read()?;
Some((*guard).clone())
}
}
#[cfg(feature = "tokio-sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
pub async fn get_cloned_async(&self) -> T
where
T: Clone,
{
let guard = self.tokio_inner.read().await;
(*guard).clone()
}
}
impl<T> SharedContainer<T> {
pub fn new(value: T) -> Self {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
SharedContainer {
std_inner: Arc::new(RwLock::new(value)),
}
}
#[cfg(feature = "tokio-sync")]
{
SharedContainer {
tokio_inner: Arc::new(RwLock::new(value)),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
SharedContainer {
wasm_inner: Rc::new(RefCell::new(value)),
}
}
}
#[cfg_attr(
feature = "tokio-sync",
doc = "WARNING: This method always returns None when using tokio-sync feature. Use read_async() instead."
)]
pub fn read(&self) -> Option<SharedReadGuard<'_, T>> {
#[cfg(feature = "tokio-sync")]
{
return None;
}
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
match self.std_inner.read() {
Ok(guard) => Some(SharedReadGuard::StdSync(guard)),
Err(_) => None,
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
match self.wasm_inner.try_borrow() {
Ok(borrow) => Some(SharedReadGuard::Single(borrow)),
Err(_) => None,
}
}
}
#[cfg_attr(
feature = "tokio-sync",
doc = "WARNING: This method always returns None when using tokio-sync feature. Use write_async() instead."
)]
pub fn write(&self) -> Option<SharedWriteGuard<'_, T>> {
#[cfg(feature = "tokio-sync")]
{
return None;
}
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
match self.std_inner.write() {
Ok(guard) => Some(SharedWriteGuard::StdSync(guard)),
Err(_) => None,
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
match self.wasm_inner.try_borrow_mut() {
Ok(borrow) => Some(SharedWriteGuard::Single(borrow)),
Err(_) => None,
}
}
}
pub fn downgrade(&self) -> WeakSharedContainer<T> {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
WeakSharedContainer {
std_inner: Arc::downgrade(&self.std_inner),
}
}
#[cfg(feature = "tokio-sync")]
{
WeakSharedContainer {
tokio_inner: Arc::downgrade(&self.tokio_inner),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
WeakSharedContainer {
wasm_inner: Rc::downgrade(&self.wasm_inner),
}
}
}
#[cfg(feature = "tokio-sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
pub async fn read_async(&self) -> SharedReadGuard<'_, T> {
let guard = self.tokio_inner.read().await;
SharedReadGuard::TokioSync(guard)
}
#[cfg(feature = "tokio-sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
pub async fn write_async(&self) -> SharedWriteGuard<'_, T> {
let guard = self.tokio_inner.write().await;
SharedWriteGuard::TokioSync(guard)
}
}
impl<T> WeakSharedContainer<T> {
pub fn upgrade(&self) -> Option<SharedContainer<T>> {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
self.std_inner
.upgrade()
.map(|inner| SharedContainer { std_inner: inner })
}
#[cfg(feature = "tokio-sync")]
{
self.tokio_inner
.upgrade()
.map(|inner| SharedContainer { tokio_inner: inner })
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
self.wasm_inner
.upgrade()
.map(|inner| SharedContainer { wasm_inner: inner })
}
}
}
pub enum SharedReadGuard<'a, T> {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
StdSync(std::sync::RwLockReadGuard<'a, T>),
#[cfg(feature = "tokio-sync")]
TokioSync(tokio::sync::RwLockReadGuard<'a, T>),
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
Single(Ref<'a, T>),
}
impl<'a, T> Deref for SharedReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
match self {
SharedReadGuard::StdSync(guard) => guard.deref(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
#[cfg(feature = "tokio-sync")]
{
match self {
SharedReadGuard::TokioSync(guard) => guard.deref(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
match self {
SharedReadGuard::Single(borrow) => borrow.deref(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
}
}
pub enum SharedWriteGuard<'a, T> {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
StdSync(std::sync::RwLockWriteGuard<'a, T>),
#[cfg(feature = "tokio-sync")]
TokioSync(tokio::sync::RwLockWriteGuard<'a, T>),
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
Single(RefMut<'a, T>),
}
impl<'a, T> Deref for SharedWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
match self {
SharedWriteGuard::StdSync(guard) => guard.deref(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
#[cfg(feature = "tokio-sync")]
{
match self {
SharedWriteGuard::TokioSync(guard) => guard.deref(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
match self {
SharedWriteGuard::Single(borrow) => borrow.deref(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
}
}
impl<'a, T> DerefMut for SharedWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
#[cfg(all(
feature = "std-sync",
not(feature = "tokio-sync"),
not(feature = "wasm-sync")
))]
{
match self {
SharedWriteGuard::StdSync(guard) => guard.deref_mut(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
#[cfg(feature = "tokio-sync")]
{
match self {
SharedWriteGuard::TokioSync(guard) => guard.deref_mut(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
#[cfg(any(
feature = "wasm-sync",
all(
target_arch = "wasm32",
not(feature = "std-sync"),
not(feature = "tokio-sync")
)
))]
{
match self {
SharedWriteGuard::Single(borrow) => borrow.deref_mut(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccessError {
UnsupportedMode,
BorrowConflict,
Poisoned,
}
impl std::fmt::Display for AccessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccessError::UnsupportedMode => {
write!(f, "operation not supported for this container mode")
}
AccessError::BorrowConflict => {
write!(f, "borrow conflict: lock already held")
}
AccessError::Poisoned => {
write!(f, "lock poisoned by panic")
}
}
}
}
impl std::error::Error for AccessError {}
pub trait SyncAccess<T> {
fn read(&self) -> Result<SyncReadGuard<'_, T>, AccessError>;
fn write(&self) -> Result<SyncWriteGuard<'_, T>, AccessError>;
fn get_cloned(&self) -> Result<T, AccessError>
where
T: Clone;
}
#[cfg(feature = "async")]
pub trait AsyncAccess<T> {
fn read_async<'a>(&'a self) -> impl std::future::Future<Output = AsyncReadGuard<'a, T>> + Send
where
T: 'a;
fn write_async<'a>(
&'a self,
) -> impl std::future::Future<Output = AsyncWriteGuard<'a, T>> + Send
where
T: 'a;
fn get_cloned_async(&self) -> impl std::future::Future<Output = T> + Send
where
T: Clone;
}
#[derive(Debug)]
pub enum SyncReadGuard<'a, T> {
#[cfg(not(target_arch = "wasm32"))]
Std(std::sync::RwLockReadGuard<'a, T>),
#[cfg(target_arch = "wasm32")]
Wasm(Ref<'a, T>),
}
impl<'a, T> Deref for SyncReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
#[cfg(not(target_arch = "wasm32"))]
SyncReadGuard::Std(guard) => guard.deref(),
#[cfg(target_arch = "wasm32")]
SyncReadGuard::Wasm(guard) => guard.deref(),
}
}
}
#[derive(Debug)]
pub enum SyncWriteGuard<'a, T> {
#[cfg(not(target_arch = "wasm32"))]
Std(std::sync::RwLockWriteGuard<'a, T>),
#[cfg(target_arch = "wasm32")]
Wasm(RefMut<'a, T>),
}
impl<'a, T> Deref for SyncWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
#[cfg(not(target_arch = "wasm32"))]
SyncWriteGuard::Std(guard) => guard.deref(),
#[cfg(target_arch = "wasm32")]
SyncWriteGuard::Wasm(guard) => guard.deref(),
}
}
}
impl<'a, T> DerefMut for SyncWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
#[cfg(not(target_arch = "wasm32"))]
SyncWriteGuard::Std(guard) => guard.deref_mut(),
#[cfg(target_arch = "wasm32")]
SyncWriteGuard::Wasm(guard) => guard.deref_mut(),
}
}
}
#[cfg(feature = "async")]
#[derive(Debug)]
pub struct AsyncReadGuard<'a, T>(tokio::sync::RwLockReadGuard<'a, T>);
#[cfg(feature = "async")]
impl<'a, T> Deref for AsyncReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
#[cfg(feature = "async")]
#[derive(Debug)]
pub struct AsyncWriteGuard<'a, T>(tokio::sync::RwLockWriteGuard<'a, T>);
#[cfg(feature = "async")]
impl<'a, T> Deref for AsyncWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
#[cfg(feature = "async")]
impl<'a, T> DerefMut for AsyncWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
#[derive(Debug)]
pub struct Shared<T> {
#[cfg(target_arch = "wasm32")]
inner: Rc<RefCell<T>>,
#[cfg(not(target_arch = "wasm32"))]
inner: std::sync::Arc<std::sync::RwLock<T>>,
}
#[derive(Debug)]
pub struct WeakShared<T> {
#[cfg(target_arch = "wasm32")]
inner: RcWeak<RefCell<T>>,
#[cfg(not(target_arch = "wasm32"))]
inner: std::sync::Weak<std::sync::RwLock<T>>,
}
#[cfg(feature = "async")]
#[derive(Debug)]
pub struct AsyncShared<T> {
inner: Arc<tokio::sync::RwLock<T>>,
}
#[cfg(feature = "async")]
unsafe impl<T: Send> Send for AsyncShared<T> {}
#[cfg(feature = "async")]
unsafe impl<T: Send + Sync> Sync for AsyncShared<T> {}
#[cfg(feature = "async")]
#[derive(Debug)]
pub struct WeakAsyncShared<T> {
inner: Weak<tokio::sync::RwLock<T>>,
}
#[derive(Debug)]
pub enum SharedAny<T> {
Sync(Shared<T>),
#[cfg(feature = "async")]
Async(AsyncShared<T>),
}
#[derive(Debug)]
pub enum WeakSharedAny<T> {
Sync(WeakShared<T>),
#[cfg(feature = "async")]
Async(WeakAsyncShared<T>),
}
impl<T> Shared<T> {
pub fn new(value: T) -> Self {
#[cfg(target_arch = "wasm32")]
{
Shared {
inner: Rc::new(RefCell::new(value)),
}
}
#[cfg(not(target_arch = "wasm32"))]
{
Shared {
inner: std::sync::Arc::new(std::sync::RwLock::new(value)),
}
}
}
pub fn downgrade(&self) -> WeakShared<T> {
#[cfg(target_arch = "wasm32")]
{
WeakShared {
inner: Rc::downgrade(&self.inner),
}
}
#[cfg(not(target_arch = "wasm32"))]
{
WeakShared {
inner: std::sync::Arc::downgrade(&self.inner),
}
}
}
}
impl<T> Clone for Shared<T> {
fn clone(&self) -> Self {
#[cfg(target_arch = "wasm32")]
{
Shared {
inner: Rc::clone(&self.inner),
}
}
#[cfg(not(target_arch = "wasm32"))]
{
Shared {
inner: std::sync::Arc::clone(&self.inner),
}
}
}
}
impl<T> WeakShared<T> {
pub fn upgrade(&self) -> Option<Shared<T>> {
#[cfg(target_arch = "wasm32")]
{
self.inner.upgrade().map(|inner| Shared { inner })
}
#[cfg(not(target_arch = "wasm32"))]
{
self.inner.upgrade().map(|inner| Shared { inner })
}
}
}
impl<T> Clone for WeakShared<T> {
fn clone(&self) -> Self {
WeakShared {
inner: self.inner.clone(),
}
}
}
impl<T> SyncAccess<T> for Shared<T> {
fn read(&self) -> Result<SyncReadGuard<'_, T>, AccessError> {
#[cfg(not(target_arch = "wasm32"))]
{
self.inner
.read()
.map(SyncReadGuard::Std)
.map_err(|_| AccessError::Poisoned)
}
#[cfg(target_arch = "wasm32")]
{
self.inner
.try_borrow()
.map(SyncReadGuard::Wasm)
.map_err(|_| AccessError::BorrowConflict)
}
}
fn write(&self) -> Result<SyncWriteGuard<'_, T>, AccessError> {
#[cfg(not(target_arch = "wasm32"))]
{
self.inner
.write()
.map(SyncWriteGuard::Std)
.map_err(|_| AccessError::Poisoned)
}
#[cfg(target_arch = "wasm32")]
{
self.inner
.try_borrow_mut()
.map(SyncWriteGuard::Wasm)
.map_err(|_| AccessError::BorrowConflict)
}
}
fn get_cloned(&self) -> Result<T, AccessError>
where
T: Clone,
{
let guard = self.read()?;
Ok((*guard).clone())
}
}
#[cfg(feature = "async")]
impl<T> AsyncShared<T> {
pub fn new(value: T) -> Self {
AsyncShared {
inner: Arc::new(tokio::sync::RwLock::new(value)),
}
}
pub fn downgrade(&self) -> WeakAsyncShared<T> {
WeakAsyncShared {
inner: Arc::downgrade(&self.inner),
}
}
}
#[cfg(feature = "async")]
impl<T> Clone for AsyncShared<T> {
fn clone(&self) -> Self {
AsyncShared {
inner: Arc::clone(&self.inner),
}
}
}
#[cfg(feature = "async")]
impl<T> WeakAsyncShared<T> {
pub fn upgrade(&self) -> Option<AsyncShared<T>> {
self.inner.upgrade().map(|inner| AsyncShared { inner })
}
}
#[cfg(feature = "async")]
impl<T> Clone for WeakAsyncShared<T> {
fn clone(&self) -> Self {
WeakAsyncShared {
inner: self.inner.clone(),
}
}
}
#[cfg(feature = "async")]
impl<T: Send + Sync> AsyncAccess<T> for AsyncShared<T> {
async fn read_async<'a>(&'a self) -> AsyncReadGuard<'a, T>
where
T: 'a,
{
AsyncReadGuard(self.inner.read().await)
}
async fn write_async<'a>(&'a self) -> AsyncWriteGuard<'a, T>
where
T: 'a,
{
AsyncWriteGuard(self.inner.write().await)
}
async fn get_cloned_async(&self) -> T
where
T: Clone,
{
let guard = self.inner.read().await;
(*guard).clone()
}
}
impl<T> From<Shared<T>> for SharedAny<T> {
fn from(shared: Shared<T>) -> Self {
SharedAny::Sync(shared)
}
}
#[cfg(feature = "async")]
impl<T> From<AsyncShared<T>> for SharedAny<T> {
fn from(shared: AsyncShared<T>) -> Self {
SharedAny::Async(shared)
}
}
impl<T> Clone for SharedAny<T> {
fn clone(&self) -> Self {
match self {
SharedAny::Sync(s) => SharedAny::Sync(s.clone()),
#[cfg(feature = "async")]
SharedAny::Async(a) => SharedAny::Async(a.clone()),
}
}
}
impl<T> SharedAny<T> {
pub fn downgrade(&self) -> WeakSharedAny<T> {
match self {
SharedAny::Sync(s) => WeakSharedAny::Sync(s.downgrade()),
#[cfg(feature = "async")]
SharedAny::Async(a) => WeakSharedAny::Async(a.downgrade()),
}
}
}
impl<T> WeakSharedAny<T> {
pub fn upgrade(&self) -> Option<SharedAny<T>> {
match self {
WeakSharedAny::Sync(w) => w.upgrade().map(SharedAny::Sync),
#[cfg(feature = "async")]
WeakSharedAny::Async(w) => w.upgrade().map(SharedAny::Async),
}
}
}
impl<T> Clone for WeakSharedAny<T> {
fn clone(&self) -> Self {
match self {
WeakSharedAny::Sync(w) => WeakSharedAny::Sync(w.clone()),
#[cfg(feature = "async")]
WeakSharedAny::Async(w) => WeakSharedAny::Async(w.clone()),
}
}
}
impl<T> SyncAccess<T> for SharedAny<T> {
fn read(&self) -> Result<SyncReadGuard<'_, T>, AccessError> {
match self {
SharedAny::Sync(s) => s.read(),
#[cfg(feature = "async")]
SharedAny::Async(_) => Err(AccessError::UnsupportedMode),
}
}
fn write(&self) -> Result<SyncWriteGuard<'_, T>, AccessError> {
match self {
SharedAny::Sync(s) => s.write(),
#[cfg(feature = "async")]
SharedAny::Async(_) => Err(AccessError::UnsupportedMode),
}
}
fn get_cloned(&self) -> Result<T, AccessError>
where
T: Clone,
{
match self {
SharedAny::Sync(s) => s.get_cloned(),
#[cfg(feature = "async")]
SharedAny::Async(_) => Err(AccessError::UnsupportedMode),
}
}
}
#[cfg(feature = "async")]
impl<T: Send + Sync> AsyncAccess<T> for SharedAny<T> {
async fn read_async<'a>(&'a self) -> AsyncReadGuard<'a, T>
where
T: 'a,
{
match self {
SharedAny::Async(a) => a.read_async().await,
SharedAny::Sync(_) => {
unreachable!("Cannot call async methods on sync container")
}
}
}
async fn write_async<'a>(&'a self) -> AsyncWriteGuard<'a, T>
where
T: 'a,
{
match self {
SharedAny::Async(a) => a.write_async().await,
SharedAny::Sync(_) => {
unreachable!("Cannot call async methods on sync container")
}
}
}
async fn get_cloned_async(&self) -> T
where
T: Clone,
{
match self {
SharedAny::Async(a) => a.get_cloned_async().await,
SharedAny::Sync(_) => {
unreachable!("Cannot call async methods on sync container")
}
}
}
}
#[cfg(test)]
mod tests {
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
struct TestStruct {
value: i32,
}
#[cfg(not(feature = "tokio-sync"))]
mod sync_tests {
use super::*;
use crate::SharedContainer;
#[test]
fn test_read_access() {
let container = SharedContainer::new(TestStruct { value: 42 });
let guard = container.read().unwrap();
assert_eq!(guard.value, 42);
}
#[test]
fn test_write_access() {
let container = SharedContainer::new(TestStruct { value: 42 });
{
let mut guard = container.write().unwrap();
guard.value = 100;
}
let guard = container.read().unwrap();
assert_eq!(guard.value, 100);
}
#[test]
fn test_clone_container() {
let container1 = SharedContainer::new(TestStruct { value: 42 });
let container2 = container1.clone();
{
let mut guard = container2.write().unwrap();
guard.value = 100;
}
let guard = container1.read().unwrap();
assert_eq!(guard.value, 100);
}
#[test]
fn test_get_cloned() {
let container = SharedContainer::new(TestStruct { value: 42 });
let cloned = container.get_cloned().unwrap();
assert_eq!(cloned, TestStruct { value: 42 });
{
let mut guard = container.write().unwrap();
guard.value = 100;
}
assert_eq!(cloned, TestStruct { value: 42 });
let new_clone = container.get_cloned().unwrap();
assert_eq!(new_clone, TestStruct { value: 100 });
}
#[test]
fn test_weak_ref() {
let container = SharedContainer::new(TestStruct { value: 42 });
let weak = container.downgrade();
let container2 = weak.upgrade().unwrap();
{
let mut guard = container2.write().unwrap();
guard.value = 100;
}
{
let guard = container.read().unwrap();
assert_eq!(guard.value, 100);
}
drop(container);
drop(container2);
assert!(weak.upgrade().is_none());
}
#[test]
fn test_weak_clone() {
let container = SharedContainer::new(TestStruct { value: 42 });
let weak1 = container.downgrade();
let weak2 = weak1.clone();
let container1 = weak1.upgrade().unwrap();
let container2 = weak2.upgrade().unwrap();
{
let mut guard = container2.write().unwrap();
guard.value = 100;
}
{
let guard = container1.read().unwrap();
assert_eq!(guard.value, 100);
}
drop(container);
drop(container1);
drop(container2);
assert!(weak1.upgrade().is_none());
assert!(weak2.upgrade().is_none());
}
}
}
#[cfg(test)]
#[cfg(feature = "tokio-sync")]
mod tokio_tests {
use super::*;
use tokio::runtime::Runtime;
#[derive(Debug, Clone, PartialEq)]
struct TestStruct {
value: i32,
}
#[test]
fn test_tokio_read_access() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let container = SharedContainer::new(TestStruct { value: 42 });
assert!(container.read().is_none());
let guard = container.read_async().await;
assert_eq!(guard.value, 42);
});
}
#[test]
fn test_tokio_write_access() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let container = SharedContainer::new(TestStruct { value: 42 });
assert!(container.write().is_none());
{
let mut guard = container.write_async().await;
guard.value = 100;
}
let guard = container.read_async().await;
assert_eq!(guard.value, 100);
});
}
#[test]
fn test_tokio_clone_container() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let container1 = SharedContainer::new(TestStruct { value: 42 });
let container2 = container1.clone();
{
let mut guard = container2.write_async().await;
guard.value = 100;
}
let guard = container1.read_async().await;
assert_eq!(guard.value, 100);
});
}
#[test]
fn test_tokio_weak_ref() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let container = SharedContainer::new(TestStruct { value: 42 });
let weak = container.downgrade();
let container2 = weak.upgrade().unwrap();
{
let mut guard = container2.write_async().await;
guard.value = 100;
}
{
let guard = container.read_async().await;
assert_eq!(guard.value, 100);
}
drop(container);
drop(container2);
assert!(weak.upgrade().is_none());
});
}
}
#[cfg(test)]
#[cfg(any(target_arch = "wasm32", feature = "force-wasm-impl"))]
mod wasm_tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
struct TestStruct {
value: i32,
}
#[test]
fn test_wasm_read_access() {
let container = SharedContainer::new(TestStruct { value: 42 });
let guard = container.read().unwrap();
assert_eq!(guard.value, 42);
}
#[test]
fn test_wasm_write_access() {
let container = SharedContainer::new(TestStruct { value: 42 });
{
let mut guard = container.write().unwrap();
guard.value = 100;
}
let guard = container.read().unwrap();
assert_eq!(guard.value, 100);
}
#[test]
fn test_wasm_borrow_conflict() {
let container = SharedContainer::new(TestStruct { value: 42 });
let _guard = container.read().unwrap();
assert!(container.write().is_none());
}
#[test]
fn test_wasm_multiple_reads() {
let container = SharedContainer::new(TestStruct { value: 42 });
let _guard1 = container.read().unwrap();
let guard2 = container.read().unwrap();
assert_eq!(guard2.value, 42);
}
#[test]
fn test_wasm_weak_ref() {
let container = SharedContainer::new(TestStruct { value: 42 });
let weak = container.downgrade();
let container2 = weak.upgrade().unwrap();
assert_eq!(container2.read().unwrap().value, 42);
drop(container);
drop(container2);
assert!(weak.upgrade().is_none());
}
}