Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions core/engine/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::{
realm::Realm,
};
use boa_gc::{Finalize, Trace};
use futures_channel::oneshot;
use futures_concurrency::future::FutureGroup;
use futures_lite::{StreamExt, future};
use portable_atomic::AtomicBool;
Expand All @@ -48,6 +49,7 @@ use std::mem;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::task::Poll;
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, future::Future, pin::Pin};

/// An ECMAScript [Job Abstract Closure].
Expand Down Expand Up @@ -718,7 +720,10 @@ impl JobExecutor for SimpleJobExecutor {
let now = context.borrow().clock().now();
let jobs_to_run = {
let mut timeout_jobs = self.timeout_jobs.borrow_mut();
let mut jobs_to_keep = timeout_jobs.split_off(&now);
// Use `now + 1ns` so jobs whose deadline equals `now` are
// included in `jobs_to_run` rather than deferred.
let split_at = now + time::Duration::from_nanos(1).into();
let mut jobs_to_keep = timeout_jobs.split_off(&split_at);
jobs_to_keep.retain(|_, jobs| {
jobs.retain(|job| !job.is_cancelled());
!jobs.is_empty()
Expand Down Expand Up @@ -763,7 +768,51 @@ impl JobExecutor for SimpleJobExecutor {
}
}
context.borrow_mut().clear_kept_objects();
future::yield_now().await;

let no_live_work = group.is_empty()
&& self.promise_jobs.borrow().is_empty()
&& self.async_jobs.borrow().is_empty()
&& self.generic_jobs.borrow().is_empty();

if no_live_work && !self.timeout_jobs.borrow().is_empty() {
// Only future-scheduled timeout jobs remain. Idle until the
// earliest one is due instead of busy-spinning.
let now = context.borrow().clock().now();
let deadline = self
.timeout_jobs
.borrow()
.keys()
.next()
.copied()
.filter(|&d| d > now);

if let Some(deadline) = deadline {
let dur = time::Duration::from(deadline - now);
if !dur.is_zero() {
let (tx, mut rx) = oneshot::channel::<()>();
std::thread::spawn(move || {
std::thread::sleep(dur);
let _ = tx.send(());
});
Comment on lines +793 to +796
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very inefficient. We are essentially creating a whole thread to just wait for dur milliseconds.

I don't think this is something we can fix on the SimpleJobExecutor. This executor should be kept as simple as possible, but to properly support this we need an async executor to sleep using an async timer.

// Re-check the clock on every poll so that mock clocks
// (e.g. FixedClock used in tests) can advance past the
// deadline without waiting for real-time sleep to complete.
std::future::poll_fn(|cx| {
let now = context.borrow().clock().now();
if now >= deadline {
return Poll::Ready(());
}
match Pin::new(&mut rx).poll(cx) {
Poll::Ready(_) => Poll::Ready(()),
Poll::Pending => Poll::Pending,
}
})
.await;
}
}
} else {
future::yield_now().await;
}
}

Ok(())
Expand Down
Loading