use super::*;
unsafe impl Sync for CurrentHookContextCell {}
unsafe impl Sync for MicrotaskCacheCell {}
impl Scheduler {
pub(crate) fn update(dependents: &[usize]) {
Registry::mark_dirty(dependents);
if SUPPRESS_SCHEDULE.load(Ordering::Relaxed) {
return;
}
if SCHEDULED.load(Ordering::Relaxed) {
return;
}
SCHEDULED.store(true, Ordering::Relaxed);
let window_value: Window = match window() {
Some(window_instance) => window_instance,
None => {
SCHEDULED.store(false, Ordering::Relaxed);
return;
}
};
let queued_microtask: bool = MICROTASK_CACHE.with(|cache: &MicrotaskCacheCell| {
let cache_ptr: *mut MicrotaskCache = cache.get_0().get();
let cache_ref: &MicrotaskCache = unsafe { &*cache_ptr };
if cache_ref.queue_microtask.is_none() {
if let Some(window_value_inner) = window() {
let queue_microtask_value: JsValue =
Reflect::get(&window_value_inner, &JsValue::from_str(QUEUE_MICROTASK))
.unwrap_or(JsValue::UNDEFINED);
if let Ok(queue_microtask) = queue_microtask_value.dyn_into::<Function>() {
unsafe {
(*cache_ptr).queue_microtask = Some(queue_microtask);
}
}
}
let cache_ref: &MicrotaskCache = unsafe { &*cache_ptr };
if let Some(queue_microtask) = &cache_ref.queue_microtask {
let dispatch_function: &Function = DISPATCH_CLOSURE.with(|closure| unsafe {
&*(closure.as_ref() as *const _ as *const Function)
});
return queue_microtask
.call1(&window_value, dispatch_function)
.is_ok();
}
return false;
}
let queue_microtask: &Function = match cache_ref.queue_microtask.as_ref() {
Some(queue_microtask) => queue_microtask,
None => return false,
};
let dispatch_function: &Function = DISPATCH_CLOSURE
.with(|closure| unsafe { &*(closure.as_ref() as *const _ as *const Function) });
queue_microtask
.call1(&window_value, dispatch_function)
.is_ok()
});
if queued_microtask {
return;
}
let scheduled: bool = DISPATCH_CLOSURE.with(|dispatch_closure: &Closure<dyn FnMut()>| {
let dispatch_function: &Function =
dispatch_closure.as_ref().unchecked_ref::<Function>();
window_value
.set_timeout_with_callback_and_timeout_and_arguments_0(dispatch_function, 0)
.is_ok()
});
if scheduled {
return;
}
let requested_frame: bool =
DISPATCH_CLOSURE.with(|dispatch_closure: &Closure<dyn FnMut()>| {
let dispatch_function: &Function =
dispatch_closure.as_ref().unchecked_ref::<Function>();
window_value
.request_animation_frame(dispatch_function)
.is_ok()
});
if requested_frame {
return;
}
SCHEDULED.store(false, Ordering::Relaxed);
}
pub(crate) fn batch<F, R>(callback: F) -> R
where
F: FnOnce() -> R,
{
let was_outermost: bool = !SUPPRESS_SCHEDULE.load(Ordering::Relaxed);
SUPPRESS_SCHEDULE.store(true, Ordering::Relaxed);
let result: R = callback();
SUPPRESS_SCHEDULE.store(!was_outermost, Ordering::Relaxed);
if was_outermost && Registry::has_dirty() {
Self::update(&[]);
}
result
}
pub(crate) fn dispatch_updates() {
if SIGNAL_UPDATE_DISPATCHING.load(Ordering::Relaxed) {
return;
}
SIGNAL_UPDATE_DISPATCHING.store(true, Ordering::Relaxed);
let mut iterations: usize = 0;
loop {
let dirty_keys: HashSet<usize> = take(Registry::get_mut_dirty_update_ids());
if dirty_keys.is_empty() {
break;
}
for key in dirty_keys {
let entry: SignalUpdateEntry =
match Registry::get_mut_update_registry().remove(&key) {
Some(removed_entry) => removed_entry,
None => continue,
};
let slot: &mut SignalUpdateSlot = unsafe { &mut *entry };
if slot.get_removed() {
unsafe {
let _: Box<SignalUpdateSlot> = Box::from_raw(entry);
}
continue;
}
slot.set_dirty(false);
let callback: Option<Box<dyn FnMut()>> = slot.get_mut_callback().take();
if let Some(mut callback) = callback {
callback();
let slot: &mut SignalUpdateSlot = unsafe { &mut *entry };
if !slot.get_removed() {
slot.set_callback(Some(callback));
}
}
let slot: &SignalUpdateSlot = unsafe { &*entry };
if slot.get_removed() {
unsafe {
let _: Box<SignalUpdateSlot> = Box::from_raw(entry);
}
continue;
}
let registry: &mut HashMap<usize, SignalUpdateEntry> =
Registry::get_mut_update_registry();
if registry.contains_key(&key) {
unsafe {
let _: Box<SignalUpdateSlot> = Box::from_raw(entry);
}
continue;
}
registry.insert(key, entry);
}
iterations += 1;
if iterations >= MAX_ITERATIONS {
break;
}
}
SIGNAL_UPDATE_DISPATCHING.store(false, Ordering::Relaxed);
}
}