test with forked version of debug-cell

This commit is contained in:
niedzwiedzw 2023-07-13 20:12:28 +02:00
parent 60d6137f39
commit 337697a886
3 changed files with 35 additions and 127 deletions

View file

@ -9,16 +9,18 @@ repository = "https://github.com/DioxusLabs/dioxus/"
homepage = "https://dioxuslabs.com" homepage = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react"] keywords = ["dom", "ui", "gui", "react"]
[features]
default = []
nightly-features = []
[dependencies] [dependencies]
dioxus-core = { workspace = true } dioxus-core = { workspace = true }
futures-channel = { workspace = true } futures-channel = { workspace = true }
log = { workspace = true } log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
debug-cell = { git = "https://github.com/Niedzwiedzw/debug-cell", rev = "3352a1f8aff19f56f5e3b2018200a3338fd43d2e" } # waiting for the merge / official DioxusLabs fork
[dev-dependencies] [dev-dependencies]
futures-util = { workspace = true, default-features = false } futures-util = { workspace = true, default-features = false }
dioxus-core = { workspace = true } dioxus-core = { workspace = true }
dioxus = { workspace = true } dioxus = { workspace = true }
debug-cell = "0.1.1"

View file

@ -1,3 +1,5 @@
#![cfg_attr(feature = "nightly-features", feature(debug_refcell))]
#[macro_export] #[macro_export]
/// A helper macro for using hooks and properties in async environements. /// A helper macro for using hooks and properties in async environements.
/// ///

View file

@ -1,17 +1,15 @@
use self::error::{UseSharedStateError, UseSharedStateResult}; use self::error::{UseSharedStateError, UseSharedStateResult};
use dioxus_core::{ScopeId, ScopeState}; use dioxus_core::{ScopeId, ScopeState};
use std::{ use std::{collections::HashSet, rc::Rc, sync::Arc};
cell::{Ref, RefCell, RefMut},
collections::HashSet, #[cfg(debug_assertions)]
rc::Rc, pub use debug_cell::{
sync::Arc, error::{BorrowError, BorrowMutError},
Ref, RefCell, RefMut,
}; };
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
type Location = (); pub use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
#[cfg(debug_assertions)]
type Location = &'static std::panic::Location<'static>;
#[macro_export] #[macro_export]
macro_rules! debug_location { macro_rules! debug_location {
@ -27,119 +25,49 @@ macro_rules! debug_location {
}}; }};
} }
pub mod diagnostics {
use std::panic::Location;
#[derive(Debug, Clone, Copy)]
pub enum BorrowKind {
Mutable,
Immutable,
}
#[derive(Debug, Clone, Copy)]
pub struct PreviousBorrow {
pub location: Location<'static>,
pub kind: BorrowKind,
}
impl PreviousBorrow {
pub fn display_opt(value: &Option<Self>) -> String {
value
.as_ref()
.map(|value| value.to_string())
.unwrap_or_default()
}
}
impl std::fmt::Display for PreviousBorrow {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { location, kind } = self;
write!(f, "{location} ({kind:?})")
}
}
impl<T> super::UseSharedState<T> {
#[cfg_attr(debug_assertions, track_caller)]
#[cfg_attr(debug_assertions, inline(never))]
#[allow(unused_must_use)]
pub(super) fn debug_track_borrow(&self) {
#[cfg(debug_assertions)]
self.previous_borrow
.borrow_mut()
.insert(PreviousBorrow::borrowed());
}
#[cfg_attr(debug_assertions, track_caller)]
#[cfg_attr(debug_assertions, inline(never))]
#[allow(unused_must_use)]
pub(super) fn debug_track_borrow_mut(&self) {
#[cfg(debug_assertions)]
self.previous_borrow
.borrow_mut()
.insert(PreviousBorrow::borrowed_mut());
}
}
impl PreviousBorrow {
#[track_caller]
#[inline(never)]
pub fn borrowed() -> Self {
Self {
location: *Location::caller(),
kind: BorrowKind::Immutable,
}
}
#[track_caller]
#[inline(never)]
pub fn borrowed_mut() -> Self {
Self {
location: *Location::caller(),
kind: BorrowKind::Mutable,
}
}
}
}
pub mod error { pub mod error {
fn locations_display(locations: &[&'static std::panic::Location<'static>]) -> String {
locations
.iter()
.map(|location| format!(" - {location}"))
.collect::<Vec<_>>()
.join("\n")
}
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum UseSharedStateError { pub enum UseSharedStateError {
#[cfg_attr( #[cfg_attr(
debug_assertions, debug_assertions,
error( error(
"[{location}] {type_name} is already borrowed at [{previous_borrow}], so it cannot be borrowed mutably.", "[{0}] {1} is already borrowed at, so it cannot be borrowed mutably. Previous borrows:\n[{2}]\n\n",
previous_borrow = super::diagnostics::PreviousBorrow::display_opt(.previous_borrow) .source.attempted_at,
.type_name,
locations_display(&.source.already_borrowed_at)
) )
)] )]
#[cfg_attr( #[cfg_attr(
not(debug_assertions), not(debug_assertions),
error("{type_name} is already borrowed, so it cannot be borrowed mutably.") error("{type_name} is already borrowed, so it cannot be borrowed mutably. (More detail available in debug mode)")
)] )]
AlreadyBorrowed { AlreadyBorrowed {
source: core::cell::BorrowMutError, source: super::BorrowMutError,
type_name: &'static str, type_name: &'static str,
/// Only available in debug mode
location: super::Location,
#[cfg(debug_assertions)]
previous_borrow: Option<super::diagnostics::PreviousBorrow>,
}, },
#[cfg_attr( #[cfg_attr(
debug_assertions, debug_assertions,
error( error(
"[{location}] {type_name} is already borrowed mutably at [{previous_borrow}], so it cannot be borrowed anymore.", "[{0}] {1} is already borrowed mutably at [{2}], so it cannot be borrowed anymore.",
previous_borrow = super::diagnostics::PreviousBorrow::display_opt(.previous_borrow) .source.attempted_at,
.type_name,
locations_display(&.source.already_borrowed_at)
) )
)] )]
#[cfg_attr( #[cfg_attr(
not(debug_assertions), not(debug_assertions),
error("{type_name} is already borrowed mutably, so it cannot be borrowed anymore.") error("{type_name} is already borrowed mutably, so it cannot be borrowed anymore. (More detail available in debug mode)")
)] )]
AlreadyBorrowedMutably { AlreadyBorrowedMutably {
source: core::cell::BorrowError, source: super::BorrowError,
type_name: &'static str, type_name: &'static str,
/// Only available in debug mode
location: super::Location,
#[cfg(debug_assertions)]
previous_borrow: Option<super::diagnostics::PreviousBorrow>,
}, },
} }
@ -259,17 +187,11 @@ impl<T> Drop for UseSharedStateOwner<T> {
/// State that is shared between components through the context system /// State that is shared between components through the context system
pub struct UseSharedState<T> { pub struct UseSharedState<T> {
pub(crate) inner: Rc<RefCell<ProvidedStateInner<T>>>, pub(crate) inner: Rc<RefCell<ProvidedStateInner<T>>>,
#[cfg(debug_assertions)]
previous_borrow: Rc<RefCell<Option<diagnostics::PreviousBorrow>>>,
} }
impl<T> UseSharedState<T> { impl<T> UseSharedState<T> {
fn new(inner: Rc<RefCell<ProvidedStateInner<T>>>) -> Self { fn new(inner: Rc<RefCell<ProvidedStateInner<T>>>) -> Self {
Self { Self { inner }
inner,
#[cfg(debug_assertions)]
previous_borrow: Default::default(),
}
} }
/// Notify all consumers of the state that it has changed. (This is called automatically when you call "write") /// Notify all consumers of the state that it has changed. (This is called automatically when you call "write")
@ -282,16 +204,10 @@ impl<T> UseSharedState<T> {
#[cfg_attr(debug_assertions, inline(never))] #[cfg_attr(debug_assertions, inline(never))]
pub fn try_read(&self) -> UseSharedStateResult<Ref<'_, T>> { pub fn try_read(&self) -> UseSharedStateResult<Ref<'_, T>> {
match self.inner.try_borrow() { match self.inner.try_borrow() {
Ok(value) => { Ok(value) => Ok(Ref::map(value, |inner| &inner.value)),
self.debug_track_borrow();
Ok(Ref::map(value, |inner| &inner.value))
}
Err(source) => Err(UseSharedStateError::AlreadyBorrowedMutably { Err(source) => Err(UseSharedStateError::AlreadyBorrowedMutably {
source, source,
type_name: std::any::type_name::<Self>(), type_name: std::any::type_name::<Self>(),
location: debug_location!(),
#[cfg(debug_assertions)]
previous_borrow: *self.previous_borrow.borrow(),
}), }),
} }
} }
@ -315,16 +231,12 @@ impl<T> UseSharedState<T> {
pub fn try_write(&self) -> UseSharedStateResult<RefMut<'_, T>> { pub fn try_write(&self) -> UseSharedStateResult<RefMut<'_, T>> {
match self.inner.try_borrow_mut() { match self.inner.try_borrow_mut() {
Ok(mut value) => { Ok(mut value) => {
self.debug_track_borrow_mut();
value.notify_consumers(); value.notify_consumers();
Ok(RefMut::map(value, |inner| &mut inner.value)) Ok(RefMut::map(value, |inner| &mut inner.value))
} }
Err(source) => Err(UseSharedStateError::AlreadyBorrowed { Err(source) => Err(UseSharedStateError::AlreadyBorrowed {
source, source,
type_name: std::any::type_name::<Self>(), type_name: std::any::type_name::<Self>(),
location: crate::debug_location!(),
#[cfg(debug_assertions)]
previous_borrow: *self.previous_borrow.borrow(),
}), }),
} }
} }
@ -350,16 +262,10 @@ impl<T> UseSharedState<T> {
#[cfg_attr(debug_assertions, inline(never))] #[cfg_attr(debug_assertions, inline(never))]
pub fn try_write_silent(&self) -> UseSharedStateResult<RefMut<'_, T>> { pub fn try_write_silent(&self) -> UseSharedStateResult<RefMut<'_, T>> {
match self.inner.try_borrow_mut() { match self.inner.try_borrow_mut() {
Ok(value) => { Ok(value) => Ok(RefMut::map(value, |inner| &mut inner.value)),
self.debug_track_borrow_mut();
Ok(RefMut::map(value, |inner| &mut inner.value))
}
Err(source) => Err(UseSharedStateError::AlreadyBorrowed { Err(source) => Err(UseSharedStateError::AlreadyBorrowed {
source, source,
type_name: std::any::type_name::<Self>(), type_name: std::any::type_name::<Self>(),
location: crate::debug_location!(),
#[cfg(debug_assertions)]
previous_borrow: *self.previous_borrow.borrow(),
}), }),
} }
} }
@ -382,8 +288,6 @@ impl<T> Clone for UseSharedState<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
inner: self.inner.clone(), inner: self.inner.clone(),
#[cfg(debug_assertions)]
previous_borrow: self.previous_borrow.clone(),
} }
} }
} }