diff --git a/oden-js/src/runtime.rs b/oden-js/src/runtime.rs index 1719f3a0..e349d955 100644 --- a/oden-js/src/runtime.rs +++ b/oden-js/src/runtime.rs @@ -175,37 +175,44 @@ impl Runtime { /// Process all pending async jobs. This includes all promise resolutions. fn process_promise_completions(&self) { - // TODO: This could be more robust if we buffered all the completed - // promise entries and then dropped the borrow of the state, so - // that we never invoked user code while borrowing our private - // state mutably. + let mut promises = vec![]; + + // First, gather all the completed promises into a temporary vector + // so that we only need to borrow our mutable state for a short + // period of time. let mut state = unsafe { PrivateState::from_rt_mut(self.rt) }; while let Ok((handle, evt)) = state.promise_recv.try_recv() { if let Some(entry) = state.promise_table.remove(&handle) { - let ctx = ContextRef::from_raw(entry.context); - let (callback, value) = match evt { - PromiseEvent::Resolved(v) => (entry.resolve, v), - PromiseEvent::Rejected(v) => (entry.reject, v), - }; - - // Convert the result into a JS value, which we can only - // really do while we are on this thread. - let value = value(&ctx).expect("Should be able to convert promise result to value"); - - // Call the particular callback and make sure it doesn't throw. - ctx.check_exception(unsafe { - let mut args = [value.val]; - sys::JS_Call( - entry.context, - callback, - sys::JS_MakeUndefined(), - 1, - args.as_mut_ptr(), - ) - }) - .expect("Exception thrown by promise callback"); + promises.push((entry, evt)); } } + drop(state); // Don't need our internal state anymore. + + // Nowe we can complete all the promises. + for (entry, evt) in promises { + let ctx = ContextRef::from_raw(entry.context); + let (callback, value) = match evt { + PromiseEvent::Resolved(v) => (entry.resolve, v), + PromiseEvent::Rejected(v) => (entry.reject, v), + }; + + // Convert the result into a JS value, which we can only + // really do while we are on this thread. + let value = value(&ctx).expect("Should be able to convert promise result to value"); + + // Call the particular callback and make sure it doesn't throw. + ctx.check_exception(unsafe { + let mut args = [value.val]; + sys::JS_Call( + entry.context, + callback, + sys::JS_MakeUndefined(), + 1, + args.as_mut_ptr(), + ) + }) + .expect("Exception thrown by promise callback"); + } } /// Process all pending async jobs. This includes all promise resolutions.