mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 12:43:08 +00:00
Fix race condition in use_future
This commit is contained in:
parent
72a2a1f820
commit
c954a116eb
1 changed files with 21 additions and 94 deletions
|
@ -1,12 +1,8 @@
|
|||
#![allow(missing_docs)]
|
||||
use dioxus_core::{ScopeState, TaskId};
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
future::{Future, IntoFuture},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc};
|
||||
|
||||
use crate::{use_state, UseState};
|
||||
|
||||
/// A future that resolves to a value.
|
||||
///
|
||||
|
@ -31,43 +27,30 @@ where
|
|||
F: Future<Output = T> + 'static,
|
||||
D: UseFutureDep,
|
||||
{
|
||||
let val = use_state(cx, || None);
|
||||
|
||||
let state = cx.use_hook(move || UseFuture {
|
||||
update: cx.schedule_update(),
|
||||
needs_regen: Cell::new(true),
|
||||
values: Default::default(),
|
||||
task: Cell::new(None),
|
||||
state: val.clone(),
|
||||
task: Default::default(),
|
||||
dependencies: Vec::new(),
|
||||
waker: Default::default(),
|
||||
});
|
||||
|
||||
*state.waker.borrow_mut() = None;
|
||||
|
||||
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() {
|
||||
// We don't need regen anymore
|
||||
state.needs_regen.set(false);
|
||||
// kill the old one, if it exists
|
||||
if let Some(task) = state.task.take() {
|
||||
cx.remove_future(task);
|
||||
}
|
||||
|
||||
// Create the new future
|
||||
let fut = future(dependencies.out());
|
||||
|
||||
// Clone in our cells
|
||||
let values = state.values.clone();
|
||||
let schedule_update = state.update.clone();
|
||||
let waker = state.waker.clone();
|
||||
|
||||
// Cancel the current future
|
||||
if let Some(current) = state.task.take() {
|
||||
cx.remove_future(current);
|
||||
}
|
||||
let val = val.clone();
|
||||
let task = state.task.clone();
|
||||
|
||||
state.task.set(Some(cx.push_future(async move {
|
||||
let res = fut.await;
|
||||
values.borrow_mut().push(Box::leak(Box::new(res)));
|
||||
|
||||
// if there's a waker, we dont re-render the component. Instead we just progress that future
|
||||
match waker.borrow().as_ref() {
|
||||
Some(waker) => waker.wake_by_ref(),
|
||||
None => schedule_update(),
|
||||
}
|
||||
val.set(Some(fut.await));
|
||||
task.take();
|
||||
})));
|
||||
}
|
||||
|
||||
|
@ -80,21 +63,12 @@ pub enum FutureState<'a, T> {
|
|||
Regenerating(&'a T), // the old value
|
||||
}
|
||||
|
||||
pub struct UseFuture<T> {
|
||||
pub struct UseFuture<T: 'static> {
|
||||
update: Arc<dyn Fn()>,
|
||||
needs_regen: Cell<bool>,
|
||||
task: Cell<Option<TaskId>>,
|
||||
task: Rc<Cell<Option<TaskId>>>,
|
||||
dependencies: Vec<Box<dyn Any>>,
|
||||
waker: Rc<RefCell<Option<std::task::Waker>>>,
|
||||
values: Rc<RefCell<Vec<*mut T>>>,
|
||||
}
|
||||
|
||||
impl<T> Drop for UseFuture<T> {
|
||||
fn drop(&mut self) {
|
||||
for value in self.values.take().into_iter() {
|
||||
drop(unsafe { Box::from_raw(value) })
|
||||
}
|
||||
}
|
||||
state: UseState<Option<T>>,
|
||||
}
|
||||
|
||||
pub enum UseFutureState<'a, T> {
|
||||
|
@ -120,30 +94,16 @@ impl<T> UseFuture<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// clears the value in the future slot without starting the future over
|
||||
pub fn clear(&self) -> Option<T> {
|
||||
todo!()
|
||||
// (self.update)();
|
||||
// self.slot.replace(None)
|
||||
}
|
||||
|
||||
// Manually set the value in the future slot without starting the future over
|
||||
pub fn set(&self, _new_value: T) {
|
||||
// self.slot.set(Some(new_value));
|
||||
// self.needs_regen.set(true);
|
||||
// (self.update)();
|
||||
todo!()
|
||||
pub fn set(&self, new_value: T) {
|
||||
self.state.set(Some(new_value));
|
||||
}
|
||||
|
||||
/// Return any value, even old values if the future has not yet resolved.
|
||||
///
|
||||
/// If the future has never completed, the returned value will be `None`.
|
||||
pub fn value(&self) -> Option<&T> {
|
||||
self.values
|
||||
.borrow_mut()
|
||||
.last()
|
||||
.cloned()
|
||||
.map(|x| unsafe { &*x })
|
||||
self.state.current_val.as_ref().as_ref()
|
||||
}
|
||||
|
||||
/// Get the ID of the future in Dioxus' internal scheduler
|
||||
|
@ -169,35 +129,6 @@ impl<T> UseFuture<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoFuture for &'a UseFuture<T> {
|
||||
type Output = &'a T;
|
||||
type IntoFuture = UseFutureAwait<'a, T>;
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
UseFutureAwait { hook: self }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UseFutureAwait<'a, T> {
|
||||
hook: &'a UseFuture<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Future for UseFutureAwait<'a, T> {
|
||||
type Output = &'a T;
|
||||
|
||||
fn poll(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
match self.hook.values.borrow_mut().last().cloned() {
|
||||
Some(value) => std::task::Poll::Ready(unsafe { &*value }),
|
||||
None => {
|
||||
self.hook.waker.replace(Some(cx.waker().clone()));
|
||||
std::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UseFutureDep: Sized + Clone {
|
||||
type Out;
|
||||
fn out(&self) -> Self::Out;
|
||||
|
@ -343,10 +274,6 @@ mod tests {
|
|||
let blah = "asd";
|
||||
});
|
||||
|
||||
let g2 = a.await;
|
||||
|
||||
let g = fut.await;
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue