mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
map_each deadlock prevention
map_each previously was written such that if a chain of mappings fed each other, a deadlock could occur because while the first one was mapped, the second callback gets invoked and tries to update the first value while it's still being held. This refactor switches from std Mutex to parking_lot, allowing me to remove a workaround for needing to run drop callbacks in a separate thread during the drop of a DynamicGuard. In addition to that change, the lower level `map_generational` calls now take a DynamicGuard as their parameter. This allows these functions to drop ownership of the referenced data during the callback. The map_each implementation takes advantage of this by ensuring that the guard is dropped before set is invoked, minimizing potential lock overlaps. With this refactor, some old code of mine with complex validations now works again.
This commit is contained in:
parent
beede55f0a
commit
20ae2b7c72
14 changed files with 214 additions and 164 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -639,6 +639,7 @@ dependencies = [
|
||||||
"kludgine",
|
"kludgine",
|
||||||
"nominals",
|
"nominals",
|
||||||
"palette",
|
"palette",
|
||||||
|
"parking_lot",
|
||||||
"plotters",
|
"plotters",
|
||||||
"png",
|
"png",
|
||||||
"pollster",
|
"pollster",
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ png = "0.17.10"
|
||||||
image = { version = "0.25.0", features = ["png"] }
|
image = { version = "0.25.0", features = ["png"] }
|
||||||
plotters = { version = "0.3.5", default-features = false, optional = true }
|
plotters = { version = "0.3.5", default-features = false, optional = true }
|
||||||
nominals = "0.3.0"
|
nominals = "0.3.0"
|
||||||
|
parking_lot = "0.12.1"
|
||||||
|
|
||||||
|
|
||||||
# [patch.crates-io]
|
# [patch.crates-io]
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,7 @@ fn main() -> cushy::Result {
|
||||||
"Inline Widgets"
|
"Inline Widgets"
|
||||||
.and(callback_widget())
|
.and(callback_widget())
|
||||||
.into_rows()
|
.into_rows()
|
||||||
.and(
|
.and("impl MakeWidget".and(ToggleMakeWidget).into_rows())
|
||||||
"impl MakeWidget"
|
|
||||||
.and(ToggleMakeWidget::default())
|
|
||||||
.into_rows(),
|
|
||||||
)
|
|
||||||
.and("impl Widget".and(impl_widget()).into_rows())
|
.and("impl Widget".and(impl_widget()).into_rows())
|
||||||
.into_columns()
|
.into_columns()
|
||||||
.centered()
|
.centered()
|
||||||
|
|
@ -59,7 +55,7 @@ fn callback_widget() -> impl MakeWidgetWithTag {
|
||||||
/// widget or any of its children aren't focusable, implementing [`MakeWidget`]
|
/// widget or any of its children aren't focusable, implementing [`MakeWidget`]
|
||||||
/// directly will make more sense.
|
/// directly will make more sense.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ToggleMakeWidget(Toggle);
|
struct ToggleMakeWidget;
|
||||||
|
|
||||||
impl MakeWidgetWithTag for ToggleMakeWidget {
|
impl MakeWidgetWithTag for ToggleMakeWidget {
|
||||||
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
|
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ use std::cmp::Ordering;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::ops::{ControlFlow, Deref, Div, DivAssign, Mul, MulAssign, Sub};
|
use std::ops::{ControlFlow, Deref, Div, DivAssign, Mul, MulAssign, Sub};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Condvar, Mutex, MutexGuard, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
|
@ -53,10 +53,11 @@ use figures::{Angle, Point, Ranged, Rect, Size, UnscaledUnit, Zero};
|
||||||
use intentional::Cast;
|
use intentional::Cast;
|
||||||
use kempt::Set;
|
use kempt::Set;
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
use parking_lot::{Condvar, Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::animation::easings::Linear;
|
use crate::animation::easings::Linear;
|
||||||
use crate::styles::{Component, RequireInvalidation};
|
use crate::styles::{Component, RequireInvalidation};
|
||||||
use crate::utils::{run_in_bg, IgnorePoison};
|
use crate::utils::run_in_bg;
|
||||||
use crate::value::{Destination, Dynamic, Source};
|
use crate::value::{Destination, Dynamic, Source};
|
||||||
|
|
||||||
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
|
static ANIMATIONS: Mutex<Animating> = Mutex::new(Animating::new());
|
||||||
|
|
@ -67,7 +68,7 @@ fn thread_state() -> MutexGuard<'static, Animating> {
|
||||||
THREAD.get_or_init(|| {
|
THREAD.get_or_init(|| {
|
||||||
thread::spawn(animation_thread);
|
thread::spawn(animation_thread);
|
||||||
});
|
});
|
||||||
ANIMATIONS.lock().ignore_poison()
|
ANIMATIONS.lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn animation_thread() {
|
fn animation_thread() {
|
||||||
|
|
@ -75,7 +76,7 @@ fn animation_thread() {
|
||||||
loop {
|
loop {
|
||||||
if state.running.is_empty() {
|
if state.running.is_empty() {
|
||||||
state.last_updated = None;
|
state.last_updated = None;
|
||||||
state = NEW_ANIMATIONS.wait(state).ignore_poison();
|
NEW_ANIMATIONS.wait(&mut state);
|
||||||
} else {
|
} else {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let last_tick = state.last_updated.unwrap_or(start);
|
let last_tick = state.last_updated.unwrap_or(start);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use arboard::Clipboard;
|
use arboard::Clipboard;
|
||||||
use kludgine::app::{AppEvent, AsApplication};
|
use kludgine::app::{AppEvent, AsApplication};
|
||||||
|
use parking_lot::{Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::fonts::FontCollection;
|
use crate::fonts::FontCollection;
|
||||||
use crate::utils::IgnorePoison;
|
|
||||||
use crate::window::sealed::WindowCommand;
|
use crate::window::sealed::WindowCommand;
|
||||||
use crate::window::WindowHandle;
|
use crate::window::WindowHandle;
|
||||||
|
|
||||||
|
|
@ -64,9 +64,7 @@ impl Cushy {
|
||||||
/// initialized when the window opened.
|
/// initialized when the window opened.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
|
pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
|
||||||
self.clipboard
|
self.clipboard.as_ref().map(|mutex| mutex.lock())
|
||||||
.as_ref()
|
|
||||||
.map(|mutex| mutex.lock().ignore_poison())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the font collection that will be loaded in all Cushy windows.
|
/// Returns the font collection that will be loaded in all Cushy windows.
|
||||||
|
|
|
||||||
|
|
@ -1414,11 +1414,11 @@ impl<T> Trackable for T where T: sealed::Trackable {}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use kempt::Set;
|
use kempt::Set;
|
||||||
|
use parking_lot::{Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::utils::IgnorePoison;
|
|
||||||
use crate::widget::WidgetId;
|
use crate::widget::WidgetId;
|
||||||
use crate::window::WindowHandle;
|
use crate::window::WindowHandle;
|
||||||
|
|
||||||
|
|
@ -1445,12 +1445,12 @@ pub(crate) mod sealed {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate(&self, widget: WidgetId) -> bool {
|
pub fn invalidate(&self, widget: WidgetId) -> bool {
|
||||||
let mut invalidated = self.invalidated.lock().ignore_poison();
|
let mut invalidated = self.invalidated.lock();
|
||||||
invalidated.insert(widget)
|
invalidated.insert(widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidations(&self) -> MutexGuard<'_, Set<WidgetId>> {
|
pub fn invalidations(&self) -> MutexGuard<'_, Set<WidgetId>> {
|
||||||
self.invalidated.lock().ignore_poison()
|
self.invalidated.lock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ahash::AHashSet;
|
use ahash::AHashSet;
|
||||||
|
|
@ -8,9 +8,9 @@ use figures::Point;
|
||||||
use intentional::Assert;
|
use intentional::Assert;
|
||||||
use kludgine::app::winit::event::{ElementState, MouseButton};
|
use kludgine::app::winit::event::{ElementState, MouseButton};
|
||||||
use kludgine::app::winit::keyboard::Key;
|
use kludgine::app::winit::keyboard::Key;
|
||||||
|
use parking_lot::{Condvar, Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::context::WidgetContext;
|
use crate::context::WidgetContext;
|
||||||
use crate::utils::IgnorePoison;
|
|
||||||
use crate::value::{Destination, Dynamic};
|
use crate::value::{Destination, Dynamic};
|
||||||
use crate::widget::{EventHandling, HANDLED, IGNORED};
|
use crate::widget::{EventHandling, HANDLED, IGNORED};
|
||||||
use crate::window::KeyEvent;
|
use crate::window::KeyEvent;
|
||||||
|
|
@ -170,7 +170,7 @@ struct TickData {
|
||||||
|
|
||||||
impl TickData {
|
impl TickData {
|
||||||
fn state(&self) -> MutexGuard<'_, TickState> {
|
fn state(&self) -> MutexGuard<'_, TickState> {
|
||||||
self.state.lock().ignore_poison()
|
self.state.lock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,7 +218,7 @@ where
|
||||||
while state.keep_running {
|
while state.keep_running {
|
||||||
let current_frame = data.rendered_frame.load(Ordering::Acquire);
|
let current_frame = data.rendered_frame.load(Ordering::Acquire);
|
||||||
if state.frame == current_frame {
|
if state.frame == current_frame {
|
||||||
state = data.sync.wait(state).ignore_poison();
|
data.sync.wait(&mut state);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
src/tree.rs
69
src/tree.rs
|
|
@ -1,13 +1,13 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use alot::{LotId, Lots};
|
use alot::{LotId, Lots};
|
||||||
use figures::units::{Px, UPx};
|
use figures::units::{Px, UPx};
|
||||||
use figures::{Point, Rect, Size};
|
use figures::{Point, Rect, Size};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::styles::{Styles, ThemePair, VisualOrder};
|
use crate::styles::{Styles, ThemePair, VisualOrder};
|
||||||
use crate::utils::IgnorePoison;
|
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::widget::{MountedWidget, WidgetId, WidgetInstance};
|
use crate::widget::{MountedWidget, WidgetId, WidgetInstance};
|
||||||
use crate::window::{ThemeMode, WindowHandle};
|
use crate::window::{ThemeMode, WindowHandle};
|
||||||
|
|
@ -24,7 +24,7 @@ impl Tree {
|
||||||
widget: WidgetInstance,
|
widget: WidgetInstance,
|
||||||
parent: Option<&MountedWidget>,
|
parent: Option<&MountedWidget>,
|
||||||
) -> MountedWidget {
|
) -> MountedWidget {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
let id = widget.id();
|
let id = widget.id();
|
||||||
let (effective_styles, parent_id) = if let Some(parent) = parent {
|
let (effective_styles, parent_id) = if let Some(parent) = parent {
|
||||||
(
|
(
|
||||||
|
|
@ -72,7 +72,7 @@ impl Tree {
|
||||||
parent: &MountedWidget,
|
parent: &MountedWidget,
|
||||||
children_to_unmount: &mut Vec<WidgetId>,
|
children_to_unmount: &mut Vec<WidgetId>,
|
||||||
) {
|
) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.remove_child(child.node_id, parent.node_id, children_to_unmount);
|
data.remove_child(child.node_id, parent.node_id, children_to_unmount);
|
||||||
|
|
||||||
if child.widget.is_default() {
|
if child.widget.is_default() {
|
||||||
|
|
@ -84,7 +84,7 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_layout(&self, widget: LotId, rect: Rect<Px>) {
|
pub(crate) fn set_layout(&self, widget: LotId, rect: Rect<Px>) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
|
|
||||||
let node = &mut data.nodes[widget];
|
let node = &mut data.nodes[widget];
|
||||||
node.layout = Some(rect);
|
node.layout = Some(rect);
|
||||||
|
|
@ -104,12 +104,12 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn layout(&self, widget: LotId) -> Option<Rect<Px>> {
|
pub(crate) fn layout(&self, widget: LotId) -> Option<Rect<Px>> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
data.nodes.get(widget).and_then(|widget| widget.layout)
|
data.nodes.get(widget).and_then(|widget| widget.layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_frame(&self, invalidations: impl IntoIterator<Item = WidgetId>) {
|
pub(crate) fn new_frame(&self, invalidations: impl IntoIterator<Item = WidgetId>) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.render_info.clear();
|
data.render_info.clear();
|
||||||
|
|
||||||
for id in invalidations {
|
for id in invalidations {
|
||||||
|
|
@ -122,7 +122,7 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn note_widget_rendered(&self, widget: LotId) {
|
pub(crate) fn note_widget_rendered(&self, widget: LotId) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
let Some(layout) = data.nodes.get(widget).and_then(|node| node.layout) else {
|
let Some(layout) = data.nodes.get(widget).and_then(|node| node.layout) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -134,7 +134,7 @@ impl Tree {
|
||||||
parent: LotId,
|
parent: LotId,
|
||||||
constraints: Size<ConstraintLimit>,
|
constraints: Size<ConstraintLimit>,
|
||||||
) -> Option<Size<UPx>> {
|
) -> Option<Size<UPx>> {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
|
|
||||||
let node = &mut data.nodes[parent];
|
let node = &mut data.nodes[parent];
|
||||||
if let Some(cached_layout) = &node.last_layout_query {
|
if let Some(cached_layout) = &node.last_layout_query {
|
||||||
|
|
@ -161,7 +161,7 @@ impl Tree {
|
||||||
constraints: Size<ConstraintLimit>,
|
constraints: Size<ConstraintLimit>,
|
||||||
size: Size<UPx>,
|
size: Size<UPx>,
|
||||||
) {
|
) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.nodes[id].last_layout_query = Some(CachedLayoutQuery { constraints, size });
|
data.nodes[id].last_layout_query = Some(CachedLayoutQuery { constraints, size });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,7 +170,7 @@ impl Tree {
|
||||||
parent: LotId,
|
parent: LotId,
|
||||||
order: VisualOrder,
|
order: VisualOrder,
|
||||||
) -> Vec<MountedWidget> {
|
) -> Vec<MountedWidget> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
let node = &data.nodes[parent];
|
let node = &data.nodes[parent];
|
||||||
let mut unordered = node.children.clone();
|
let mut unordered = node.children.clone();
|
||||||
let mut ordered = Vec::<MountedWidget>::with_capacity(unordered.len());
|
let mut ordered = Vec::<MountedWidget>::with_capacity(unordered.len());
|
||||||
|
|
@ -230,12 +230,12 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn effective_styles(&self, id: LotId) -> Styles {
|
pub(crate) fn effective_styles(&self, id: LotId) -> Styles {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
data.nodes[id].effective_styles.clone()
|
data.nodes[id].effective_styles.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hover(&self, new_hover: Option<&MountedWidget>) -> HoverResults {
|
pub(crate) fn hover(&self, new_hover: Option<&MountedWidget>) -> HoverResults {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
let hovered = new_hover
|
let hovered = new_hover
|
||||||
.map(|new_hover| data.widget_hierarchy(new_hover.node_id, self))
|
.map(|new_hover| data.widget_hierarchy(new_hover.node_id, self))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
@ -260,12 +260,12 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(&self, new_focus: Option<WidgetId>) -> Result<Option<MountedWidget>, ()> {
|
pub fn focus(&self, new_focus: Option<WidgetId>) -> Result<Option<MountedWidget>, ()> {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.update_tracked_widget(new_focus, self, |data| &mut data.focus)
|
data.update_tracked_widget(new_focus, self, |data| &mut data.focus)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous_focus(&self, focus: WidgetId) -> Option<MountedWidget> {
|
pub fn previous_focus(&self, focus: WidgetId) -> Option<MountedWidget> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
let previous = *data.previous_focuses.get(&focus)?;
|
let previous = *data.previous_focuses.get(&focus)?;
|
||||||
data.widget_from_id(previous, self)
|
data.widget_from_id(previous, self)
|
||||||
}
|
}
|
||||||
|
|
@ -274,24 +274,24 @@ impl Tree {
|
||||||
&self,
|
&self,
|
||||||
new_active: Option<&MountedWidget>,
|
new_active: Option<&MountedWidget>,
|
||||||
) -> Result<Option<MountedWidget>, ()> {
|
) -> Result<Option<MountedWidget>, ()> {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.update_tracked_widget(new_active.map(MountedWidget::id), self, |data| {
|
data.update_tracked_widget(new_active.map(MountedWidget::id), self, |data| {
|
||||||
&mut data.active
|
&mut data.active
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn widget(&self, id: WidgetId) -> Option<MountedWidget> {
|
pub fn widget(&self, id: WidgetId) -> Option<MountedWidget> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
data.widget_from_id(id, self)
|
data.widget_from_id(id, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn widget_from_node(&self, id: LotId) -> Option<MountedWidget> {
|
pub(crate) fn widget_from_node(&self, id: LotId) -> Option<MountedWidget> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
data.widget_from_node(id, self)
|
data.widget_from_node(id, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_enabled(&self, mut id: LotId, context: &WindowHandle) -> bool {
|
pub(crate) fn is_enabled(&self, mut id: LotId, context: &WindowHandle) -> bool {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
loop {
|
loop {
|
||||||
let Some(node) = data.nodes.get(id) else {
|
let Some(node) = data.nodes.get(id) else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -310,23 +310,23 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn active_widget(&self) -> Option<LotId> {
|
pub(crate) fn active_widget(&self) -> Option<LotId> {
|
||||||
self.data.lock().ignore_poison().active
|
self.data.lock().active
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hovered_widget(&self) -> Option<LotId> {
|
pub(crate) fn hovered_widget(&self) -> Option<LotId> {
|
||||||
self.data.lock().ignore_poison().hover
|
self.data.lock().hover
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_widget(&self) -> Option<LotId> {
|
pub(crate) fn default_widget(&self) -> Option<LotId> {
|
||||||
self.data.lock().ignore_poison().defaults.last().copied()
|
self.data.lock().defaults.last().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn escape_widget(&self) -> Option<LotId> {
|
pub(crate) fn escape_widget(&self) -> Option<LotId> {
|
||||||
self.data.lock().ignore_poison().escapes.last().copied()
|
self.data.lock().escapes.last().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_hovered(&self, id: LotId) -> bool {
|
pub(crate) fn is_hovered(&self, id: LotId) -> bool {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
let mut search = data.hover;
|
let mut search = data.hover;
|
||||||
while let Some(hovered) = search {
|
while let Some(hovered) = search {
|
||||||
if hovered == id {
|
if hovered == id {
|
||||||
|
|
@ -339,21 +339,21 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn focused_widget(&self) -> Option<LotId> {
|
pub(crate) fn focused_widget(&self) -> Option<LotId> {
|
||||||
self.data.lock().ignore_poison().focus
|
self.data.lock().focus
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn widgets_under_point(&self, point: Point<Px>) -> Vec<MountedWidget> {
|
pub(crate) fn widgets_under_point(&self, point: Point<Px>) -> Vec<MountedWidget> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
data.render_info.widgets_under_point(point, &data, self)
|
data.render_info.widgets_under_point(point, &data, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parent(&self, id: LotId) -> Option<LotId> {
|
pub(crate) fn parent(&self, id: LotId) -> Option<LotId> {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
data.nodes.get(id).expect("missing widget").parent
|
data.nodes.get(id).expect("missing widget").parent
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_child(&self, mut id: LotId, possible_parent: &WidgetInstance) -> bool {
|
pub(crate) fn is_child(&self, mut id: LotId, possible_parent: &WidgetInstance) -> bool {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
while let Some(node) = data.nodes.get(id) {
|
while let Some(node) = data.nodes.get(id) {
|
||||||
if &node.widget == possible_parent {
|
if &node.widget == possible_parent {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -371,17 +371,17 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_styles(&self, id: LotId, styles: Value<Styles>) {
|
pub(crate) fn attach_styles(&self, id: LotId, styles: Value<Styles>) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.attach_styles(id, styles);
|
data.attach_styles(id, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_theme(&self, id: LotId, theme: Value<ThemePair>) {
|
pub(crate) fn attach_theme(&self, id: LotId, theme: Value<ThemePair>) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.nodes.get_mut(id).expect("missing widget").theme = Some(theme);
|
data.nodes.get_mut(id).expect("missing widget").theme = Some(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_theme_mode(&self, id: LotId, theme: Value<ThemeMode>) {
|
pub(crate) fn attach_theme_mode(&self, id: LotId, theme: Value<ThemeMode>) {
|
||||||
let mut data = self.data.lock().ignore_poison();
|
let mut data = self.data.lock();
|
||||||
data.nodes.get_mut(id).expect("missing widget").theme_mode = Some(theme);
|
data.nodes.get_mut(id).expect("missing widget").theme_mode = Some(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,7 +389,7 @@ impl Tree {
|
||||||
&self,
|
&self,
|
||||||
id: LotId,
|
id: LotId,
|
||||||
) -> (Styles, Option<Value<ThemePair>>, Option<Value<ThemeMode>>) {
|
) -> (Styles, Option<Value<ThemePair>>, Option<Value<ThemeMode>>) {
|
||||||
let data = self.data.lock().ignore_poison();
|
let data = self.data.lock();
|
||||||
let node = data.nodes.get(id).expect("missing widget");
|
let node = data.nodes.get(id).expect("missing widget");
|
||||||
(
|
(
|
||||||
node.effective_styles.clone(),
|
node.effective_styles.clone(),
|
||||||
|
|
@ -399,10 +399,7 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate(&self, id: LotId, include_hierarchy: bool) {
|
pub fn invalidate(&self, id: LotId, include_hierarchy: bool) {
|
||||||
self.data
|
self.data.lock().invalidate(id, include_hierarchy);
|
||||||
.lock()
|
|
||||||
.ignore_poison()
|
|
||||||
.invalidate(id, include_hierarchy);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
15
src/utils.rs
15
src/utils.rs
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::mpsc::{self, SyncSender};
|
use std::sync::mpsc::{self, SyncSender};
|
||||||
use std::sync::{OnceLock, PoisonError};
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use intentional::Assert;
|
use intentional::Assert;
|
||||||
use kludgine::app::winit::event::Modifiers;
|
use kludgine::app::winit::event::Modifiers;
|
||||||
|
|
@ -225,19 +225,6 @@ impl<T> Deref for Lazy<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IgnorePoison {
|
|
||||||
type Unwrapped;
|
|
||||||
fn ignore_poison(self) -> Self::Unwrapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IgnorePoison for Result<T, PoisonError<T>> {
|
|
||||||
type Unwrapped = T;
|
|
||||||
|
|
||||||
fn ignore_poison(self) -> Self::Unwrapped {
|
|
||||||
self.map_or_else(PoisonError::into_inner, |g| g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait BgFunction: FnOnce() + Send + 'static {}
|
pub trait BgFunction: FnOnce() + Send + 'static {}
|
||||||
|
|
||||||
pub fn run_in_bg<F>(f: F)
|
pub fn run_in_bg<F>(f: F)
|
||||||
|
|
|
||||||
216
src/value.rs
216
src/value.rs
|
|
@ -7,7 +7,7 @@ use std::future::Future;
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::ops::{Add, AddAssign, Deref, DerefMut, Not};
|
use std::ops::{Add, AddAssign, Deref, DerefMut, Not};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Condvar, Mutex, MutexGuard, TryLockError, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::task::{Poll, Waker};
|
use std::task::{Poll, Waker};
|
||||||
use std::thread::{self, ThreadId};
|
use std::thread::{self, ThreadId};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
@ -16,10 +16,11 @@ use ahash::AHashSet;
|
||||||
use alot::{LotId, Lots};
|
use alot::{LotId, Lots};
|
||||||
use intentional::Assert;
|
use intentional::Assert;
|
||||||
use kempt::{Map, Sort};
|
use kempt::{Map, Sort};
|
||||||
|
use parking_lot::{Condvar, Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::animation::{AnimationHandle, DynamicTransition, IntoAnimate, LinearInterpolate, Spawn};
|
use crate::animation::{AnimationHandle, DynamicTransition, IntoAnimate, LinearInterpolate, Spawn};
|
||||||
use crate::context::{self, Trackable, WidgetContext};
|
use crate::context::{self, Trackable, WidgetContext};
|
||||||
use crate::utils::{run_in_bg, IgnorePoison, WithClone};
|
use crate::utils::WithClone;
|
||||||
use crate::widget::{
|
use crate::widget::{
|
||||||
MakeWidget, MakeWidgetWithTag, OnceCallback, WidgetId, WidgetInstance, WidgetList,
|
MakeWidget, MakeWidgetWithTag, OnceCallback, WidgetId, WidgetInstance, WidgetList,
|
||||||
};
|
};
|
||||||
|
|
@ -32,7 +33,7 @@ pub trait Source<T> {
|
||||||
/// [`Generation`].
|
/// [`Generation`].
|
||||||
fn try_map_generational<R>(
|
fn try_map_generational<R>(
|
||||||
&self,
|
&self,
|
||||||
map: impl FnOnce(&GenerationalValue<T>) -> R,
|
map: impl FnOnce(DynamicGuard<'_, T, true>) -> R,
|
||||||
) -> Result<R, DeadlockError>;
|
) -> Result<R, DeadlockError>;
|
||||||
|
|
||||||
/// Maps the contents with read-only access, providing access to the value's
|
/// Maps the contents with read-only access, providing access to the value's
|
||||||
|
|
@ -42,7 +43,7 @@ pub trait Source<T> {
|
||||||
///
|
///
|
||||||
/// This function panics if this value is already locked by the current
|
/// This function panics if this value is already locked by the current
|
||||||
/// thread.
|
/// thread.
|
||||||
fn map_generational<R>(&self, map: impl FnOnce(&GenerationalValue<T>) -> R) -> R {
|
fn map_generational<R>(&self, map: impl FnOnce(DynamicGuard<'_, T, true>) -> R) -> R {
|
||||||
self.try_map_generational(map).expect("deadlocked")
|
self.try_map_generational(map).expect("deadlocked")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +55,7 @@ pub trait Source<T> {
|
||||||
/// thread.
|
/// thread.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn generation(&self) -> Generation {
|
fn generation(&self) -> Generation {
|
||||||
self.map_generational(GenerationalValue::generation)
|
self.map_generational(|g| g.generation())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps the contents with read-only access.
|
/// Maps the contents with read-only access.
|
||||||
|
|
@ -64,7 +65,7 @@ pub trait Source<T> {
|
||||||
/// This function panics if this value is already locked by the current
|
/// This function panics if this value is already locked by the current
|
||||||
/// thread.
|
/// thread.
|
||||||
fn map_ref<R>(&self, map: impl FnOnce(&T) -> R) -> R {
|
fn map_ref<R>(&self, map: impl FnOnce(&T) -> R) -> R {
|
||||||
self.map_generational(|gen| map(&gen.value))
|
self.map_generational(|gen| map(&*gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a clone of the currently contained value.
|
/// Returns a clone of the currently contained value.
|
||||||
|
|
@ -83,7 +84,7 @@ pub trait Source<T> {
|
||||||
|
|
||||||
/// Maps the contents with read-only access.
|
/// Maps the contents with read-only access.
|
||||||
fn try_map_ref<R>(&self, map: impl FnOnce(&T) -> R) -> Result<R, DeadlockError> {
|
fn try_map_ref<R>(&self, map: impl FnOnce(&T) -> R) -> Result<R, DeadlockError> {
|
||||||
self.try_map_generational(|gen| map(&gen.value))
|
self.try_map_generational(|gen| map(&*gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a clone of the currently contained value.
|
/// Returns a clone of the currently contained value.
|
||||||
|
|
@ -91,7 +92,7 @@ pub trait Source<T> {
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
self.try_map_generational(|gen| gen.value.clone())
|
self.try_map_generational(|gen| gen.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a clone of the currently contained value.
|
/// Returns a clone of the currently contained value.
|
||||||
|
|
@ -138,7 +139,7 @@ pub trait Source<T> {
|
||||||
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> Result<(), CallbackDisconnected>
|
F: for<'a> FnMut(DynamicGuard<'_, T, true>) -> Result<(), CallbackDisconnected>
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static;
|
+ 'static;
|
||||||
|
|
||||||
|
|
@ -147,7 +148,7 @@ pub trait Source<T> {
|
||||||
fn for_each_generational<F>(&self, mut for_each: F) -> CallbackHandle
|
fn for_each_generational<F>(&self, mut for_each: F) -> CallbackHandle
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) + Send + 'static,
|
F: for<'a> FnMut(DynamicGuard<'_, T, true>) + Send + 'static,
|
||||||
{
|
{
|
||||||
self.for_each_generational_try(move |value| {
|
self.for_each_generational_try(move |value| {
|
||||||
for_each(value);
|
for_each(value);
|
||||||
|
|
@ -165,7 +166,7 @@ pub trait Source<T> {
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a T) -> Result<(), CallbackDisconnected> + Send + 'static,
|
F: for<'a> FnMut(&'a T) -> Result<(), CallbackDisconnected> + Send + 'static,
|
||||||
{
|
{
|
||||||
self.for_each_generational_try(move |gen| for_each(&gen.value))
|
self.for_each_generational_try(move |gen| for_each(&*gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attaches `for_each` to this value so that it is invoked each time the
|
/// Attaches `for_each` to this value so that it is invoked each time the
|
||||||
|
|
@ -248,7 +249,7 @@ pub trait Source<T> {
|
||||||
fn map_each_generational<R, F>(&self, mut map: F) -> Dynamic<R>
|
fn map_each_generational<R, F>(&self, mut map: F) -> Dynamic<R>
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> R + Send + 'static,
|
F: for<'a> FnMut(DynamicGuard<'a, T, true>) -> R + Send + 'static,
|
||||||
R: PartialEq + Send + 'static,
|
R: PartialEq + Send + 'static,
|
||||||
{
|
{
|
||||||
let mapped = Dynamic::new(self.map_generational(&mut map));
|
let mapped = Dynamic::new(self.map_generational(&mut map));
|
||||||
|
|
@ -269,7 +270,7 @@ pub trait Source<T> {
|
||||||
F: for<'a> FnMut(&'a T) -> R + Send + 'static,
|
F: for<'a> FnMut(&'a T) -> R + Send + 'static,
|
||||||
R: PartialEq + Send + 'static,
|
R: PartialEq + Send + 'static,
|
||||||
{
|
{
|
||||||
self.map_each_generational(move |gen| map(&gen.value))
|
self.map_each_generational(move |gen| map(&*gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new dynamic value that contains the result of invoking `map`
|
/// Creates a new dynamic value that contains the result of invoking `map`
|
||||||
|
|
@ -520,16 +521,20 @@ pub trait Destination<T> {
|
||||||
impl<T> Source<T> for Arc<DynamicData<T>> {
|
impl<T> Source<T> for Arc<DynamicData<T>> {
|
||||||
fn try_map_generational<R>(
|
fn try_map_generational<R>(
|
||||||
&self,
|
&self,
|
||||||
map: impl FnOnce(&GenerationalValue<T>) -> R,
|
map: impl FnOnce(DynamicGuard<'_, T, true>) -> R,
|
||||||
) -> Result<R, DeadlockError> {
|
) -> Result<R, DeadlockError> {
|
||||||
let state = self.state()?;
|
let state = self.state()?;
|
||||||
Ok(map(&state.wrapped))
|
Ok(map(DynamicGuard {
|
||||||
|
guard: DynamicOrOwnedGuard::Dynamic(state),
|
||||||
|
accessed_mut: false,
|
||||||
|
prevent_notifications: false,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_each_generational_try<F>(&self, mut for_each: F) -> CallbackHandle
|
fn for_each_generational_try<F>(&self, mut for_each: F) -> CallbackHandle
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> Result<(), CallbackDisconnected>
|
F: for<'a> FnMut(DynamicGuard<'a, T, true>) -> Result<(), CallbackDisconnected>
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
|
@ -550,7 +555,7 @@ impl<T> Source<T> for Arc<DynamicData<T>> {
|
||||||
dynamic_for_each(self, move || {
|
dynamic_for_each(self, move || {
|
||||||
let this = this.upgrade().ok_or(CallbackDisconnected)?;
|
let this = this.upgrade().ok_or(CallbackDisconnected)?;
|
||||||
|
|
||||||
if let Ok(value) = this.try_map_generational(GenerationalValue::clone) {
|
if let Ok(value) = this.try_map_generational(|g| g.guard.clone()) {
|
||||||
for_each(value)?;
|
for_each(value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -562,7 +567,7 @@ impl<T> Source<T> for Arc<DynamicData<T>> {
|
||||||
impl<T> Source<T> for Dynamic<T> {
|
impl<T> Source<T> for Dynamic<T> {
|
||||||
fn try_map_generational<R>(
|
fn try_map_generational<R>(
|
||||||
&self,
|
&self,
|
||||||
map: impl FnOnce(&GenerationalValue<T>) -> R,
|
map: impl FnOnce(DynamicGuard<'_, T, true>) -> R,
|
||||||
) -> Result<R, DeadlockError> {
|
) -> Result<R, DeadlockError> {
|
||||||
self.0.try_map_generational(map)
|
self.0.try_map_generational(map)
|
||||||
}
|
}
|
||||||
|
|
@ -570,7 +575,7 @@ impl<T> Source<T> for Dynamic<T> {
|
||||||
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> Result<(), CallbackDisconnected>
|
F: for<'a> FnMut(DynamicGuard<'_, T, true>) -> Result<(), CallbackDisconnected>
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
|
@ -589,10 +594,10 @@ impl<T> Source<T> for Dynamic<T> {
|
||||||
impl<T> Source<T> for DynamicReader<T> {
|
impl<T> Source<T> for DynamicReader<T> {
|
||||||
fn try_map_generational<R>(
|
fn try_map_generational<R>(
|
||||||
&self,
|
&self,
|
||||||
map: impl FnOnce(&GenerationalValue<T>) -> R,
|
map: impl FnOnce(DynamicGuard<'_, T, true>) -> R,
|
||||||
) -> Result<R, DeadlockError> {
|
) -> Result<R, DeadlockError> {
|
||||||
self.source.try_map_generational(|generational| {
|
self.source.try_map_generational(|generational| {
|
||||||
*self.read_generation.lock().ignore_poison() = generational.generation;
|
*self.read_generation.lock() = generational.generation();
|
||||||
map(generational)
|
map(generational)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -600,7 +605,7 @@ impl<T> Source<T> for DynamicReader<T> {
|
||||||
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> Result<(), CallbackDisconnected>
|
F: for<'a> FnMut(DynamicGuard<'_, T, true>) -> Result<(), CallbackDisconnected>
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
|
@ -735,19 +740,23 @@ impl<T> Owned<T> {
|
||||||
impl<T> Source<T> for Owned<T> {
|
impl<T> Source<T> for Owned<T> {
|
||||||
fn try_map_generational<R>(
|
fn try_map_generational<R>(
|
||||||
&self,
|
&self,
|
||||||
map: impl FnOnce(&GenerationalValue<T>) -> R,
|
map: impl FnOnce(DynamicGuard<'_, T, true>) -> R,
|
||||||
) -> Result<R, DeadlockError> {
|
) -> Result<R, DeadlockError> {
|
||||||
Ok(map(&self.wrapped.borrow()))
|
Ok(map(DynamicGuard {
|
||||||
|
guard: DynamicOrOwnedGuard::Owned(self.wrapped.borrow_mut()),
|
||||||
|
accessed_mut: false,
|
||||||
|
prevent_notifications: false,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
fn for_each_generational_try<F>(&self, for_each: F) -> CallbackHandle
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> Result<(), CallbackDisconnected>
|
F: for<'a> FnMut(DynamicGuard<'a, T, true>) -> Result<(), CallbackDisconnected>
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
let mut callbacks = self.callbacks.active.lock().ignore_poison();
|
let mut callbacks = self.callbacks.active.lock();
|
||||||
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
|
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
|
||||||
id: Some(callbacks.push(Box::new(for_each))),
|
id: Some(callbacks.push(Box::new(for_each))),
|
||||||
owner: None,
|
owner: None,
|
||||||
|
|
@ -760,7 +769,7 @@ impl<T> Source<T> for Owned<T> {
|
||||||
T: Clone + Send + 'static,
|
T: Clone + Send + 'static,
|
||||||
F: FnMut(GenerationalValue<T>) -> Result<(), CallbackDisconnected> + Send + 'static,
|
F: FnMut(GenerationalValue<T>) -> Result<(), CallbackDisconnected> + Send + 'static,
|
||||||
{
|
{
|
||||||
self.for_each_generational_try(move |value| for_each(value.clone()))
|
self.for_each_generational_try(move |gen| for_each(gen.guard.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -775,7 +784,9 @@ where
|
||||||
&mut updated,
|
&mut updated,
|
||||||
));
|
));
|
||||||
if updated {
|
if updated {
|
||||||
self.callbacks.invoke(&self.wrapped.borrow());
|
self.callbacks.invoke(&mut &self.wrapped, |wrapped| {
|
||||||
|
DynamicOrOwnedGuard::Owned(wrapped.borrow_mut())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
@ -829,7 +840,9 @@ where
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.accessed_mut {
|
if self.accessed_mut {
|
||||||
self.owned.callbacks.invoke(&self.borrowed);
|
self.owned.callbacks.invoke(&mut self.borrowed, |borrowed| {
|
||||||
|
DynamicOrOwnedGuard::OwnedRef(&mut *borrowed)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -850,9 +863,21 @@ impl<T> OwnedCallbacks<T>
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
pub fn invoke(&self, value: &GenerationalValue<T>) {
|
pub fn invoke<'a, U>(
|
||||||
let mut callbacks = self.active.lock().ignore_poison();
|
&self,
|
||||||
callbacks.drain_filter(|callback| callback.updated(value).is_err());
|
user: &'a mut U,
|
||||||
|
value: impl for<'b> Fn(&'b mut U) -> DynamicOrOwnedGuard<'b, T>,
|
||||||
|
) {
|
||||||
|
let mut callbacks = self.active.lock();
|
||||||
|
callbacks.drain_filter(|callback| {
|
||||||
|
callback
|
||||||
|
.updated(DynamicGuard {
|
||||||
|
guard: value(user),
|
||||||
|
accessed_mut: false,
|
||||||
|
prevent_notifications: false,
|
||||||
|
})
|
||||||
|
.is_err()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -861,19 +886,21 @@ where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
fn remove(&self, id: LotId) {
|
fn remove(&self, id: LotId) {
|
||||||
self.active.lock().ignore_poison().remove(id);
|
self.active.lock().remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait OwnedCallbackFn<T>: Send + 'static {
|
trait OwnedCallbackFn<T>: Send + 'static {
|
||||||
fn updated(&mut self, value: &GenerationalValue<T>) -> Result<(), CallbackDisconnected>;
|
fn updated(&mut self, value: DynamicGuard<'_, T, true>) -> Result<(), CallbackDisconnected>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T> OwnedCallbackFn<T> for F
|
impl<F, T> OwnedCallbackFn<T> for F
|
||||||
where
|
where
|
||||||
F: for<'a> FnMut(&'a GenerationalValue<T>) -> Result<(), CallbackDisconnected> + Send + 'static,
|
F: for<'a> FnMut(DynamicGuard<'a, T, true>) -> Result<(), CallbackDisconnected>
|
||||||
|
+ Send
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
fn updated(&mut self, value: &GenerationalValue<T>) -> Result<(), CallbackDisconnected> {
|
fn updated(&mut self, value: DynamicGuard<'_, T, true>) -> Result<(), CallbackDisconnected> {
|
||||||
self(value)
|
self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1104,7 +1131,7 @@ impl<T> Dynamic<T> {
|
||||||
/// dynamic.
|
/// dynamic.
|
||||||
pub fn try_lock(&self) -> Result<DynamicGuard<'_, T>, DeadlockError> {
|
pub fn try_lock(&self) -> Result<DynamicGuard<'_, T>, DeadlockError> {
|
||||||
Ok(DynamicGuard {
|
Ok(DynamicGuard {
|
||||||
guard: self.0.state()?,
|
guard: DynamicOrOwnedGuard::Dynamic(self.0.state()?),
|
||||||
accessed_mut: false,
|
accessed_mut: false,
|
||||||
prevent_notifications: false,
|
prevent_notifications: false,
|
||||||
})
|
})
|
||||||
|
|
@ -1112,7 +1139,7 @@ impl<T> Dynamic<T> {
|
||||||
|
|
||||||
fn lock_inner<const READONLY: bool>(&self) -> DynamicGuard<'_, T, READONLY> {
|
fn lock_inner<const READONLY: bool>(&self) -> DynamicGuard<'_, T, READONLY> {
|
||||||
DynamicGuard {
|
DynamicGuard {
|
||||||
guard: self.0.state().expect("deadlocked"),
|
guard: DynamicOrOwnedGuard::Dynamic(self.0.state().expect("deadlocked")),
|
||||||
accessed_mut: false,
|
accessed_mut: false,
|
||||||
prevent_notifications: false,
|
prevent_notifications: false,
|
||||||
}
|
}
|
||||||
|
|
@ -1344,9 +1371,19 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DynamicMutexGuard<'a, T> {
|
||||||
|
fn unlocked(&mut self, while_unlocked: impl FnOnce()) {
|
||||||
|
MutexGuard::unlocked(&mut self.guard, || {
|
||||||
|
let current_state = self.dynamic.during_callback_state.lock().take();
|
||||||
|
while_unlocked();
|
||||||
|
*self.dynamic.during_callback_state.lock() = current_state;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T> Drop for DynamicMutexGuard<'a, T> {
|
impl<'a, T> Drop for DynamicMutexGuard<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut during_state = self.dynamic.during_callback_state.lock().ignore_poison();
|
let mut during_state = self.dynamic.during_callback_state.lock();
|
||||||
*during_state = None;
|
*during_state = None;
|
||||||
drop(during_state);
|
drop(during_state);
|
||||||
self.dynamic.sync.notify_all();
|
self.dynamic.sync.notify_all();
|
||||||
|
|
@ -1379,20 +1416,19 @@ struct DynamicData<T> {
|
||||||
|
|
||||||
impl<T> DynamicData<T> {
|
impl<T> DynamicData<T> {
|
||||||
fn state(&self) -> Result<DynamicMutexGuard<'_, T>, DeadlockError> {
|
fn state(&self) -> Result<DynamicMutexGuard<'_, T>, DeadlockError> {
|
||||||
let mut during_sync = self.during_callback_state.lock().ignore_poison();
|
let mut during_sync = self.during_callback_state.lock();
|
||||||
|
|
||||||
let current_thread_id = std::thread::current().id();
|
let current_thread_id = std::thread::current().id();
|
||||||
let guard = loop {
|
let guard = loop {
|
||||||
match self.state.try_lock() {
|
match self.state.try_lock() {
|
||||||
Ok(g) => break g,
|
Some(g) => break g,
|
||||||
Err(TryLockError::Poisoned(poision)) => break poision.into_inner(),
|
None => loop {
|
||||||
Err(TryLockError::WouldBlock) => loop {
|
|
||||||
match &*during_sync {
|
match &*during_sync {
|
||||||
Some(state) if state.locked_thread == current_thread_id => {
|
Some(state) if state.locked_thread == current_thread_id => {
|
||||||
return Err(DeadlockError)
|
return Err(DeadlockError)
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
during_sync = self.sync.wait(during_sync).ignore_poison();
|
self.sync.wait(&mut during_sync);
|
||||||
}
|
}
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
|
|
@ -1443,7 +1479,7 @@ where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
{
|
{
|
||||||
let state = this.state().expect("deadlocked");
|
let state = this.state().expect("deadlocked");
|
||||||
let mut data = state.callbacks.callbacks.lock().ignore_poison();
|
let mut data = state.callbacks.callbacks.lock();
|
||||||
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
|
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
|
||||||
id: Some(data.callbacks.push(Box::new(map))),
|
id: Some(data.callbacks.push(Box::new(map))),
|
||||||
owner: Some(this.clone()),
|
owner: Some(this.clone()),
|
||||||
|
|
@ -1816,7 +1852,7 @@ struct ChangeCallbacksData {
|
||||||
|
|
||||||
impl CallbackCollection for ChangeCallbacksData {
|
impl CallbackCollection for ChangeCallbacksData {
|
||||||
fn remove(&self, id: LotId) {
|
fn remove(&self, id: LotId) {
|
||||||
let mut data = self.callbacks.lock().ignore_poison();
|
let mut data = self.callbacks.lock();
|
||||||
data.callbacks.remove(id);
|
data.callbacks.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1842,7 +1878,7 @@ struct ChangeCallbacks {
|
||||||
|
|
||||||
impl Drop for ChangeCallbacks {
|
impl Drop for ChangeCallbacks {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut currently_executing = self.data.currently_executing.lock().expect("lock poisoned");
|
let mut currently_executing = self.data.currently_executing.lock();
|
||||||
let current_thread = thread::current().id();
|
let current_thread = thread::current().id();
|
||||||
loop {
|
loop {
|
||||||
match &*currently_executing {
|
match &*currently_executing {
|
||||||
|
|
@ -1854,7 +1890,7 @@ impl Drop for ChangeCallbacks {
|
||||||
drop(currently_executing);
|
drop(currently_executing);
|
||||||
|
|
||||||
// Invoke the callbacks
|
// Invoke the callbacks
|
||||||
let mut state = self.data.callbacks.lock().ignore_poison();
|
let mut state = self.data.callbacks.lock();
|
||||||
// If the callbacks have already been invoked by another
|
// If the callbacks have already been invoked by another
|
||||||
// thread such that the callbacks observed the value our
|
// thread such that the callbacks observed the value our
|
||||||
// thread wrote, we can skip the callbacks.
|
// thread wrote, we can skip the callbacks.
|
||||||
|
|
@ -1870,8 +1906,7 @@ impl Drop for ChangeCallbacks {
|
||||||
|
|
||||||
// Remove ourselves as the current executor, notifying any
|
// Remove ourselves as the current executor, notifying any
|
||||||
// other threads that are waiting.
|
// other threads that are waiting.
|
||||||
currently_executing =
|
currently_executing = self.data.currently_executing.lock();
|
||||||
self.data.currently_executing.lock().expect("lock poisoned");
|
|
||||||
*currently_executing = None;
|
*currently_executing = None;
|
||||||
drop(currently_executing);
|
drop(currently_executing);
|
||||||
self.data.sync.notify_all();
|
self.data.sync.notify_all();
|
||||||
|
|
@ -1886,11 +1921,7 @@ impl Drop for ChangeCallbacks {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
currently_executing = self
|
self.data.sync.wait(&mut currently_executing);
|
||||||
.data
|
|
||||||
.sync
|
|
||||||
.wait(currently_executing)
|
|
||||||
.expect("lock poisoned");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1964,13 +1995,57 @@ impl<T> DerefMut for GenerationalValue<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum DynamicOrOwnedGuard<'a, T> {
|
||||||
|
Dynamic(DynamicMutexGuard<'a, T>),
|
||||||
|
Owned(RefMut<'a, GenerationalValue<T>>),
|
||||||
|
OwnedRef(&'a mut GenerationalValue<T>),
|
||||||
|
}
|
||||||
|
impl<'a, T> DynamicOrOwnedGuard<'a, T> {
|
||||||
|
fn note_changed(&mut self) -> Option<ChangeCallbacks> {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(guard) => Some(guard.note_changed()),
|
||||||
|
Self::Owned(_) | Self::OwnedRef(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlocked(&mut self, while_unlocked: impl FnOnce()) {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(guard) => guard.unlocked(while_unlocked),
|
||||||
|
Self::Owned(_) | Self::OwnedRef(_) => while_unlocked(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Deref for DynamicOrOwnedGuard<'a, T> {
|
||||||
|
type Target = GenerationalValue<T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(guard) => &guard.wrapped,
|
||||||
|
Self::Owned(r) => r,
|
||||||
|
Self::OwnedRef(r) => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DerefMut for DynamicOrOwnedGuard<'a, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(guard) => &mut guard.wrapped,
|
||||||
|
Self::Owned(r) => r,
|
||||||
|
Self::OwnedRef(r) => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An exclusive reference to the contents of a [`Dynamic`].
|
/// An exclusive reference to the contents of a [`Dynamic`].
|
||||||
///
|
///
|
||||||
/// If the contents are accessed through [`DerefMut`], all obververs will be
|
/// If the contents are accessed through [`DerefMut`], all obververs will be
|
||||||
/// notified of a change when this guard is dropped.
|
/// notified of a change when this guard is dropped.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DynamicGuard<'a, T, const READONLY: bool = false> {
|
pub struct DynamicGuard<'a, T, const READONLY: bool = false> {
|
||||||
guard: DynamicMutexGuard<'a, T>,
|
guard: DynamicOrOwnedGuard<'a, T>,
|
||||||
accessed_mut: bool,
|
accessed_mut: bool,
|
||||||
prevent_notifications: bool,
|
prevent_notifications: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -1982,7 +2057,7 @@ impl<T, const READONLY: bool> DynamicGuard<'_, T, READONLY> {
|
||||||
/// will remain unchanged while the guard is held.
|
/// will remain unchanged while the guard is held.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn generation(&self) -> Generation {
|
pub fn generation(&self) -> Generation {
|
||||||
self.guard.wrapped.generation
|
self.guard.generation
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prevent any access through [`DerefMut`] from triggering change
|
/// Prevent any access through [`DerefMut`] from triggering change
|
||||||
|
|
@ -1996,22 +2071,22 @@ impl<'a, T, const READONLY: bool> Deref for DynamicGuard<'a, T, READONLY> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.guard.wrapped.value
|
&self.guard.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> DerefMut for DynamicGuard<'a, T, false> {
|
impl<'a, T> DerefMut for DynamicGuard<'a, T, false> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.accessed_mut = true;
|
self.accessed_mut = true;
|
||||||
&mut self.guard.wrapped.value
|
&mut self.guard.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const READONLY: bool> Drop for DynamicGuard<'_, T, READONLY> {
|
impl<T, const READONLY: bool> Drop for DynamicGuard<'_, T, READONLY> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.accessed_mut && !self.prevent_notifications {
|
if self.accessed_mut && !self.prevent_notifications {
|
||||||
let mut callbacks = Some(self.guard.note_changed());
|
let callbacks = self.guard.note_changed();
|
||||||
run_in_bg(move || drop(callbacks.take()));
|
self.guard.unlocked(|| drop(callbacks));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2103,7 +2178,7 @@ impl<T> DynamicReader<T> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn lock(&self) -> DynamicGuard<'_, T, true> {
|
pub fn lock(&self) -> DynamicGuard<'_, T, true> {
|
||||||
DynamicGuard {
|
DynamicGuard {
|
||||||
guard: self.source.state().expect("deadlocked"),
|
guard: DynamicOrOwnedGuard::Dynamic(self.source.state().expect("deadlocked")),
|
||||||
accessed_mut: false,
|
accessed_mut: false,
|
||||||
prevent_notifications: false,
|
prevent_notifications: false,
|
||||||
}
|
}
|
||||||
|
|
@ -2113,7 +2188,7 @@ impl<T> DynamicReader<T> {
|
||||||
/// reader.
|
/// reader.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn read_generation(&self) -> Generation {
|
pub fn read_generation(&self) -> Generation {
|
||||||
*self.read_generation.lock().ignore_poison()
|
*self.read_generation.lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the dynamic has been modified since the last time the
|
/// Returns true if the dynamic has been modified since the last time the
|
||||||
|
|
@ -2142,13 +2217,12 @@ impl<T> DynamicReader<T> {
|
||||||
self.source
|
self.source
|
||||||
.during_callback_state
|
.during_callback_state
|
||||||
.lock()
|
.lock()
|
||||||
.ignore_poison()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, |state| state.locked_thread
|
.map_or(true, |state| state.locked_thread
|
||||||
!= std::thread::current().id()),
|
!= std::thread::current().id()),
|
||||||
"deadlocked"
|
"deadlocked"
|
||||||
);
|
);
|
||||||
let mut state = self.source.state.lock().ignore_poison();
|
let mut state = self.source.state.lock();
|
||||||
loop {
|
loop {
|
||||||
if state.wrapped.generation != self.read_generation() {
|
if state.wrapped.generation != self.read_generation() {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -2159,14 +2233,14 @@ impl<T> DynamicReader<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a notification of a change, which is synch
|
// Wait for a notification of a change, which is synch
|
||||||
state = self.source.sync.wait(state).ignore_poison();
|
self.source.sync.wait(&mut state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this reader still has any writers connected to it.
|
/// Returns true if this reader still has any writers connected to it.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn connected(&self) -> bool {
|
pub fn connected(&self) -> bool {
|
||||||
let state = self.source.state.lock().ignore_poison();
|
let state = self.source.state.lock();
|
||||||
state.readers < Arc::strong_count(&self.source) && state.on_disconnect.is_some()
|
state.readers < Arc::strong_count(&self.source) && state.on_disconnect.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2936,7 +3010,7 @@ macro_rules! impl_tuple_for_each {
|
||||||
move |$var: &$type| {
|
move |$var: &$type| {
|
||||||
$(let $rvar = $rvar.read();)+
|
$(let $rvar = $rvar.read();)+
|
||||||
let mut for_each =
|
let mut for_each =
|
||||||
for_each.lock().ignore_poison();
|
for_each.lock();
|
||||||
(for_each)(($(&$avar,)+));
|
(for_each)(($(&$avar,)+));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
@ -3108,7 +3182,7 @@ macro_rules! impl_tuple_for_each_cloned {
|
||||||
$handles += $var.for_each_cloned((&$for_each, $(&$rvar,)+).with_clone(|(for_each, $($rvar,)+)| {
|
$handles += $var.for_each_cloned((&$for_each, $(&$rvar,)+).with_clone(|(for_each, $($rvar,)+)| {
|
||||||
move |$var: $type| {
|
move |$var: $type| {
|
||||||
$(let $rvar = $rvar.get();)+
|
$(let $rvar = $rvar.get();)+
|
||||||
if let Ok(mut for_each) =
|
if let Some(mut for_each) =
|
||||||
for_each.try_lock() {
|
for_each.try_lock() {
|
||||||
(for_each)(($($avar,)+));
|
(for_each)(($($avar,)+));
|
||||||
}
|
}
|
||||||
|
|
@ -3259,7 +3333,7 @@ impl Validations {
|
||||||
{
|
{
|
||||||
let validation = Dynamic::new(Validation::None);
|
let validation = Dynamic::new(Validation::None);
|
||||||
let mut message_mapping = Self::map_to_message(move |value| check(value));
|
let mut message_mapping = Self::map_to_message(move |value| check(value));
|
||||||
let error_message = dynamic.map_each_generational(move |value| message_mapping(value));
|
let error_message = dynamic.map_each_generational(move |gen| message_mapping(&gen.guard));
|
||||||
|
|
||||||
validation.set_source((&self.state, &error_message).for_each_cloned({
|
validation.set_source((&self.state, &error_message).for_each_cloned({
|
||||||
let mut f = self.generate_validation(dynamic);
|
let mut f = self.generate_validation(dynamic);
|
||||||
|
|
@ -3453,7 +3527,7 @@ impl WhenValidation<'_> {
|
||||||
let validation = Dynamic::new(Validation::None);
|
let validation = Dynamic::new(Validation::None);
|
||||||
let mut map_to_message = Validations::map_to_message(move |value| check(value));
|
let mut map_to_message = Validations::map_to_message(move |value| check(value));
|
||||||
let error_message =
|
let error_message =
|
||||||
dynamic.map_each_generational(move |generational| map_to_message(generational));
|
dynamic.map_each_generational(move |generational| map_to_message(&generational.guard));
|
||||||
let mut f = self.validations.generate_validation(dynamic);
|
let mut f = self.validations.generate_validation(dynamic);
|
||||||
let not = self.not;
|
let not = self.not;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use std::clone::Clone;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::ops::{ControlFlow, Deref, DerefMut};
|
use std::ops::{ControlFlow, Deref, DerefMut};
|
||||||
use std::sync::atomic::{self, AtomicU64};
|
use std::sync::atomic::{self, AtomicU64};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::Arc;
|
||||||
use std::{slice, vec};
|
use std::{slice, vec};
|
||||||
|
|
||||||
use alot::LotId;
|
use alot::LotId;
|
||||||
|
|
@ -15,6 +15,7 @@ use intentional::Assert;
|
||||||
use kludgine::app::winit::event::{Ime, MouseButton, MouseScrollDelta, TouchPhase};
|
use kludgine::app::winit::event::{Ime, MouseButton, MouseScrollDelta, TouchPhase};
|
||||||
use kludgine::app::winit::window::CursorIcon;
|
use kludgine::app::winit::window::CursorIcon;
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
use parking_lot::{Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::app::{Application, Open, PendingApp, Run};
|
use crate::app::{Application, Open, PendingApp, Run};
|
||||||
use crate::context::sealed::Trackable as _;
|
use crate::context::sealed::Trackable as _;
|
||||||
|
|
@ -35,7 +36,6 @@ use crate::styles::{
|
||||||
IntoDynamicComponentValue, Styles, ThemePair, VisualOrder,
|
IntoDynamicComponentValue, Styles, ThemePair, VisualOrder,
|
||||||
};
|
};
|
||||||
use crate::tree::{Tree, WeakTree};
|
use crate::tree::{Tree, WeakTree};
|
||||||
use crate::utils::IgnorePoison;
|
|
||||||
use crate::value::{Dynamic, Generation, IntoDynamic, IntoValue, Validation, Value};
|
use crate::value::{Dynamic, Generation, IntoDynamic, IntoValue, Validation, Value};
|
||||||
use crate::widgets::checkbox::{Checkable, CheckboxState};
|
use crate::widgets::checkbox::{Checkable, CheckboxState};
|
||||||
use crate::widgets::layers::{OverlayLayer, Tooltipped};
|
use crate::widgets::layers::{OverlayLayer, Tooltipped};
|
||||||
|
|
@ -1404,8 +1404,8 @@ pub struct WidgetInstance {
|
||||||
impl Debug for WidgetInstance {
|
impl Debug for WidgetInstance {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.data.widget.try_lock() {
|
match self.data.widget.try_lock() {
|
||||||
Ok(widget) => widget.summarize(f),
|
Some(widget) => widget.summarize(f),
|
||||||
Err(_) => f.debug_struct("WidgetInstance").finish_non_exhaustive(),
|
None => f.debug_struct("WidgetInstance").finish_non_exhaustive(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1539,7 +1539,7 @@ impl WidgetInstance {
|
||||||
/// occur due to other widget locks being held.
|
/// occur due to other widget locks being held.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn lock(&self) -> WidgetGuard<'_> {
|
pub fn lock(&self) -> WidgetGuard<'_> {
|
||||||
WidgetGuard(self.data.widget.lock().ignore_poison())
|
WidgetGuard(self.data.widget.lock())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the id of the widget that should receive focus after this
|
/// Returns the id of the widget that should receive focus after this
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! Widgets that stack in the Z-direction.
|
//! Widgets that stack in the Z-direction.
|
||||||
|
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use alot::{LotId, OrderedLots};
|
use alot::{LotId, OrderedLots};
|
||||||
|
|
@ -9,11 +9,11 @@ use cushy::widget::{RootBehavior, WidgetInstance};
|
||||||
use figures::units::{Lp, Px, UPx};
|
use figures::units::{Lp, Px, UPx};
|
||||||
use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size, Zero};
|
use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size, Zero};
|
||||||
use intentional::Assert;
|
use intentional::Assert;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::animation::easings::EaseOutQuadradic;
|
use crate::animation::easings::EaseOutQuadradic;
|
||||||
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne};
|
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne};
|
||||||
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, Trackable};
|
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, Trackable};
|
||||||
use crate::utils::IgnorePoison;
|
|
||||||
use crate::value::{Destination, Dynamic, DynamicGuard, IntoValue, Source, Value};
|
use crate::value::{Destination, Dynamic, DynamicGuard, IntoValue, Source, Value};
|
||||||
use crate::widget::{
|
use crate::widget::{
|
||||||
Callback, MakeWidget, MountedChildren, MountedWidget, Widget, WidgetId, WidgetList, WidgetRef,
|
Callback, MakeWidget, MountedChildren, MountedWidget, Widget, WidgetId, WidgetList, WidgetRef,
|
||||||
|
|
@ -641,7 +641,7 @@ struct OverlayLayout {
|
||||||
impl Drop for OverlayLayout {
|
impl Drop for OverlayLayout {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(on_dismiss) = &self.on_dismiss {
|
if let Some(on_dismiss) = &self.on_dismiss {
|
||||||
let mut on_dismiss = on_dismiss.lock().ignore_poison();
|
let mut on_dismiss = on_dismiss.lock();
|
||||||
on_dismiss.invoke(());
|
on_dismiss.invoke(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,9 +144,7 @@ where
|
||||||
|
|
||||||
fn hover(&mut self, local: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
|
fn hover(&mut self, local: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
|
||||||
if let Some(tick) = &self.tick {
|
if let Some(tick) = &self.tick {
|
||||||
let Some(size) = context.last_layout().map(|rect| rect.size) else {
|
let size = context.last_layout().map(|rect| rect.size)?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let world =
|
let world =
|
||||||
tilemap::translate_coordinates(local, context.kludgine.scale(), self.zoom, size);
|
tilemap::translate_coordinates(local, context.kludgine.scale(), self.zoom, size);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use std::num::{NonZeroU32, TryFromIntError};
|
||||||
use std::ops::{Deref, DerefMut, Not};
|
use std::ops::{Deref, DerefMut, Not};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
use std::sync::{mpsc, Arc, Mutex, MutexGuard, OnceLock};
|
use std::sync::{mpsc, Arc, OnceLock};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
|
|
@ -37,6 +37,7 @@ use kludgine::drawing::Drawing;
|
||||||
use kludgine::shapes::Shape;
|
use kludgine::shapes::Shape;
|
||||||
use kludgine::wgpu::{self, CompositeAlphaMode, COPY_BYTES_PER_ROW_ALIGNMENT};
|
use kludgine::wgpu::{self, CompositeAlphaMode, COPY_BYTES_PER_ROW_ALIGNMENT};
|
||||||
use kludgine::{Color, DrawableExt, Kludgine, KludgineId, Origin, Texture};
|
use kludgine::{Color, DrawableExt, Kludgine, KludgineId, Origin, Texture};
|
||||||
|
use parking_lot::{Mutex, MutexGuard};
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
|
|
@ -2216,11 +2217,7 @@ impl InnerWindowHandle {
|
||||||
if let Some(handle) = pending.handle.get() {
|
if let Some(handle) = pending.handle.get() {
|
||||||
let _result = handle.send(message);
|
let _result = handle.send(message);
|
||||||
} else {
|
} else {
|
||||||
pending
|
pending.commands.lock().push(message);
|
||||||
.commands
|
|
||||||
.lock()
|
|
||||||
.expect("lock poisoned")
|
|
||||||
.push(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InnerWindowHandle::Known(handle) => {
|
InnerWindowHandle::Known(handle) => {
|
||||||
|
|
@ -2287,7 +2284,7 @@ impl PendingWindow {
|
||||||
let initialized = pending.handle.set(handle.clone());
|
let initialized = pending.handle.set(handle.clone());
|
||||||
assert!(initialized.is_ok());
|
assert!(initialized.is_ok());
|
||||||
|
|
||||||
for command in pending.commands.lock().expect("poisoned").drain(..) {
|
for command in pending.commands.lock().drain(..) {
|
||||||
let _result = handle.send(command);
|
let _result = handle.send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3170,7 +3167,7 @@ impl Capture {
|
||||||
slice.map_async(wgpu::MapMode::Read, {
|
slice.map_async(wgpu::MapMode::Read, {
|
||||||
let map_result = map_result.clone();
|
let map_result = map_result.clone();
|
||||||
move |result| {
|
move |result| {
|
||||||
*map_result.lock().assert("thread panicked") = Some(result);
|
*map_result.lock() = Some(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3179,7 +3176,7 @@ impl Capture {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
device.poll(wgpu::Maintain::Poll);
|
device.poll(wgpu::Maintain::Poll);
|
||||||
let mut result = map_result.lock().assert("thread panicked");
|
let mut result = map_result.lock();
|
||||||
if let Some(result) = result.take() {
|
if let Some(result) = result.take() {
|
||||||
result?;
|
result?;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue