Fix single shot timer leaks in the nodejs api

As discussed, don't use the persistent context approach for single
shot timers but store the callback directly in the global object
and "delete" it afterwards.
This commit is contained in:
Simon Hausmann 2020-12-19 13:49:41 +01:00
parent b42c16851a
commit 0e04149868
3 changed files with 25 additions and 20 deletions

View file

@ -27,6 +27,7 @@ css-color-parser2 = "1.0.1"
spin_on = "0.1" #FIXME: remove and delegate to async JS instead spin_on = "0.1" #FIXME: remove and delegate to async JS instead
vtable = { version = "0.1.1", path="../../../helper_crates/vtable" } vtable = { version = "0.1.1", path="../../../helper_crates/vtable" }
generativity = "1" generativity = "1"
rand = "0.7.3"
[build-dependencies] [build-dependencies]
neon-build = "0.6.0" neon-build = "0.6.0"

View file

@ -9,6 +9,7 @@
LICENSE END */ LICENSE END */
use core::cell::RefCell; use core::cell::RefCell;
use neon::prelude::*; use neon::prelude::*;
use rand::RngCore;
use sixtyfps_compilerlib::langtype::Type; use sixtyfps_compilerlib::langtype::Type;
use sixtyfps_corelib::Resource; use sixtyfps_corelib::Resource;
@ -491,26 +492,41 @@ declare_types! {
} }
} }
fn singleshot_timer_property(id: u32) -> String {
format!("$__sixtyfps_singleshot_timer_{}", id)
}
fn singleshot_timer(mut cx: FunctionContext) -> JsResult<JsValue> { fn singleshot_timer(mut cx: FunctionContext) -> JsResult<JsValue> {
let duration_in_msecs = cx.argument::<JsNumber>(0)?.value() as u64; let duration_in_msecs = cx.argument::<JsNumber>(0)?.value() as u64;
let handler = cx.argument::<JsFunction>(1)?; let handler = cx.argument::<JsFunction>(1)?;
let global_persistent_context = persistent_context::PersistentContext::global(&mut cx)?; let global_object: Handle<JsObject> = cx.global().downcast().unwrap();
let unique_timer_property = {
let mut rng = rand::thread_rng();
loop {
let id = rng.next_u32();
let key = singleshot_timer_property(id);
if global_object.get(&mut cx, &*key)?.is_a::<JsUndefined>() {
break key;
}
}
};
let handler_value = handler.as_value(&mut cx); let handler_value = handler.as_value(&mut cx);
let handler_idx = global_persistent_context.allocate(&mut cx, handler_value); global_object.set(&mut cx, &*unique_timer_property, handler_value).unwrap();
let callback = move || { let callback = move || {
run_with_global_contect(&move |cx, _| { run_with_global_contect(&move |cx, _| {
let global_persistent_context = let global_object: Handle<JsObject> = cx.global().downcast().unwrap();
persistent_context::PersistentContext::global(cx).unwrap();
global_persistent_context let callback = global_object
.get(cx, handler_idx) .get(cx, &*unique_timer_property)
.unwrap() .unwrap()
.downcast::<JsFunction>() .downcast::<JsFunction>()
.unwrap()
.call::<_, _, JsValue, _>(cx, JsUndefined::new(), vec![])
.unwrap(); .unwrap();
global_object.set(cx, &*unique_timer_property, JsUndefined::new()).unwrap();
callback.call::<_, _, JsValue, _>(cx, JsUndefined::new(), vec![]).unwrap();
}); });
}; };

View file

@ -40,16 +40,4 @@ impl<'a> PersistentContext<'a> {
pub fn from_object(cx: &mut impl Context<'a>, o: Handle<'a, JsObject>) -> NeonResult<Self> { pub fn from_object(cx: &mut impl Context<'a>, o: Handle<'a, JsObject>) -> NeonResult<Self> {
Ok(PersistentContext(o.get(cx, KEY)?.downcast_or_throw(cx)?)) Ok(PersistentContext(o.get(cx, KEY)?.downcast_or_throw(cx)?))
} }
pub fn global(cx: &mut impl Context<'a>) -> NeonResult<Self> {
let global_object: Handle<JsObject> = cx.global().downcast().unwrap();
Ok(match global_object.get(cx, KEY)?.downcast() {
Ok(array) => PersistentContext(array),
Err(_) => {
let ctx = PersistentContext::new(cx);
ctx.save_to_object(cx, global_object);
ctx
}
})
}
} }