also track previous caller in the in debug mode to show the conflicting borrow

This commit is contained in:
niedzwiedzw committed 2023-07-12 02:53:48 +02:00
1 parent 9da32d5465
commit 760d93716f
2 files changed
+126 -32

No files matched your search

+1
View File
@@ -21,3 +21,4 @@ thiserror = { workspace = true }
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"
+125 -32
View File
@@ -7,49 +7,117 @@ use std::{
sync::Arc, sync::Arc,
}; };
pub mod error { #[cfg(not(debug_assertions))]
type Location = ();
#[cfg(debug_assertions)]
type Location = &'static std::panic::Location<'static>;
#[macro_export]
macro_rules! debug_location {
() => {{
#[cfg(debug_assertions)]
{
std::panic::Location::caller()
}
#[cfg(not(debug_assertions))]
{
()
}
}};
}
pub mod diagnostics {
use std::panic::Location; use std::panic::Location;
#[macro_export] #[derive(Debug, Clone, Copy)]
macro_rules! debug_location { pub enum BorrowKind {
() => {{ Mutable,
#[cfg(debug_assertions)] Immutable,
{
Some(std::panic::Location::caller())
}
#[cfg(not(debug_assertions))]
{
None
}
}};
}
fn display_location(location: Option<&&Location<'_>>) -> String {
location
.map(|location| format!("[{location}] "))
.unwrap_or_else(String::new)
} }
#[derive(Debug, Clone, Copy)]
pub struct PreviousBorrow {
pub location: Location<'static>,
pub kind: BorrowKind,
}
impl<T> super::UseSharedState<T> {
#[cfg_attr(debug_assertions, track_caller)]
#[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)]
#[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]
pub fn borrowed() -> Self {
Self {
location: *Location::caller(),
kind: BorrowKind::Immutable,
}
}
#[track_caller]
pub fn borrowed_mut() -> Self {
Self {
location: *Location::caller(),
kind: BorrowKind::Mutable,
}
}
}
}
pub mod error {
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum UseSharedStateError { pub enum UseSharedStateError {
#[error( #[cfg_attr(
"{}{type_name} is already borrowed, so it cannot be borrowed mutably.", debug_assertions,
display_location(.location.as_ref()) error(
)] "[{location}] {type_name} is already borrowed at [{previous_borrow:?}], so it cannot be borrowed mutably."
)
)]
#[cfg_attr(
not(debug_assertions),
error("{type_name} is already borrowed, so it cannot be borrowed mutably.")
)]
AlreadyBorrowed { AlreadyBorrowed {
source: core::cell::BorrowMutError, source: core::cell::BorrowMutError,
type_name: &'static str, type_name: &'static str,
/// Only available in debug mode /// Only available in debug mode
location: Option<&'static Location<'static>>, location: super::Location,
#[cfg(debug_assertions)]
previous_borrow: Option<super::diagnostics::PreviousBorrow>,
}, },
#[error( #[cfg_attr(
"{}{type_name} is already borrowed mutably, so it cannot be borrowed anymore.", debug_assertions,
display_location(.location.as_ref()) error(
)] "[{location}] {type_name} is already borrowed mutably at [{previous_borrow:?}], so it cannot be borrowed anymore."
)
)]
#[cfg_attr(
not(debug_assertions),
error("{type_name} is already borrowed mutably, so it cannot be borrowed anymore.")
)]
AlreadyBorrowedMutably { AlreadyBorrowedMutably {
source: core::cell::BorrowError, source: core::cell::BorrowError,
type_name: &'static str, type_name: &'static str,
/// Only available in debug mode /// Only available in debug mode
location: Option<&'static Location<'static>>, location: super::Location,
#[cfg(debug_assertions)]
previous_borrow: Option<super::diagnostics::PreviousBorrow>,
}, },
} }
@@ -145,7 +213,7 @@ pub fn use_shared_state<T: 'static>(cx: &ScopeState) -> Option<&UseSharedState<T
root.borrow_mut().consumers.insert(scope_id); root.borrow_mut().consumers.insert(scope_id);
let state = UseSharedState { inner: root }; let state = UseSharedState::new(root);
let owner = UseSharedStateOwner { state, scope_id }; let owner = UseSharedStateOwner { state, scope_id };
Some(owner) Some(owner)
}); });
@@ -169,9 +237,19 @@ 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 {
Self {
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")
pub fn notify_consumers(&self) { pub fn notify_consumers(&self) {
self.inner.borrow_mut().notify_consumers(); self.inner.borrow_mut().notify_consumers();
@@ -185,9 +263,14 @@ impl<T> UseSharedState<T> {
.map_err(|source| UseSharedStateError::AlreadyBorrowedMutably { .map_err(|source| UseSharedStateError::AlreadyBorrowedMutably {
source, source,
type_name: std::any::type_name::<Self>(), type_name: std::any::type_name::<Self>(),
location: crate::debug_location!(), location: debug_location!(),
#[cfg(debug_assertions)]
previous_borrow: *self.previous_borrow.borrow(),
})
.map(|value| {
self.debug_track_borrow();
Ref::map(value, |inner| &inner.value)
}) })
.map(|value| Ref::map(value, |inner| &inner.value))
} }
/// Read the shared value /// Read the shared value
@@ -211,9 +294,12 @@ impl<T> UseSharedState<T> {
source, source,
type_name: std::any::type_name::<Self>(), type_name: std::any::type_name::<Self>(),
location: crate::debug_location!(), location: crate::debug_location!(),
#[cfg(debug_assertions)]
previous_borrow: *self.previous_borrow.borrow(),
}) })
.map(|mut value| { .map(|mut value| {
value.notify_consumers(); value.notify_consumers();
self.debug_track_borrow_mut();
RefMut::map(value, |inner| &mut inner.value) RefMut::map(value, |inner| &mut inner.value)
}) })
} }
@@ -242,8 +328,13 @@ impl<T> UseSharedState<T> {
source, source,
type_name: std::any::type_name::<Self>(), type_name: std::any::type_name::<Self>(),
location: crate::debug_location!(), location: crate::debug_location!(),
#[cfg(debug_assertions)]
previous_borrow: *self.previous_borrow.borrow(),
})
.map(|value| {
self.debug_track_borrow_mut();
RefMut::map(value, |inner| &mut inner.value)
}) })
.map(|value| RefMut::map(value, |inner| &mut inner.value))
} }
/// Writes the value without forcing a re-render /// Writes the value without forcing a re-render
@@ -263,6 +354,8 @@ 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: Default::default(),
} }
} }
} }