Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
85 changes: 52 additions & 33 deletions src/bundler/Graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,12 @@ impl<'a> Graph<'a> {
self.pending_items += self.deferred_pending;
self.deferred_pending = 0;
// Their units are back in `pending_items`.
let mut load = self.outstanding_loads.head;
while !load.is_null() {
// SAFETY: linked ⇒ arena-live; bundle thread.
unsafe {
(*load).deferred = false;
load = (*load).outstanding.next;
}
}
self.outstanding_loads.for_each(|load| {
// SAFETY: linked ⇒ arena-live. The load is still out with the
// plugin (its callback is parked in `.defer()`); `deferred` is
// this side's field (see `Load`), so nothing else is touched.
unsafe { (*load).deferred = false };
});

transpiler.drain_defer_task.init();
transpiler.drain_defer_task.schedule();
Expand All @@ -262,7 +260,7 @@ use bun_ast::SideEffects;
/// Intrusive doubly-linked membership in an [`OutstandingList`].
pub struct OutstandingLink<T> {
prev: *mut T,
pub(crate) next: *mut T,
next: *mut T,
linked: bool,
}
impl<T> Default for OutstandingLink<T> {
Expand All @@ -274,8 +272,16 @@ impl<T> Default for OutstandingLink<T> {
}
}
}
/// A linked node is shared with the side running the plugins, by field (see
/// the docs on `api::JSBundler::Resolve` / `Load`): the list owns the link and
/// nothing else, so it reaches the link through this raw projection and never
/// forms a reference to the whole node.
Comment thread
robobun marked this conversation as resolved.
Outdated
pub trait OutstandingNode: Sized {
fn link(&mut self) -> &mut OutstandingLink<Self>;
/// `&raw mut (*this).outstanding`.
///
/// # Safety
/// `this` points at a live node.
Comment thread
robobun marked this conversation as resolved.
Outdated
Comment thread
robobun marked this conversation as resolved.
unsafe fn link_raw(this: *mut Self) -> *mut OutstandingLink<Self>;
}
/// A bundle pass's outstanding plugin requests; single-threaded (bundle thread).
pub struct OutstandingList<T: OutstandingNode> {
Expand All @@ -290,40 +296,43 @@ impl<T: OutstandingNode> Default for OutstandingList<T> {
}
impl<T: OutstandingNode> OutstandingList<T> {
pub(crate) fn push(&mut self, node: *mut T) {
// SAFETY: `node` is arena-live and unlinked; bundle thread.
// SAFETY: `node` is arena-live and unlinked; the current head is
// linked ⇒ arena-live (and possibly being answered by the plugins'
// thread right now, hence only its link is touched); bundle thread.
unsafe {
let l = (*node).link();
debug_assert!(!l.linked);
l.linked = true;
l.prev = core::ptr::null_mut();
l.next = self.head;
let l = T::link_raw(node);
debug_assert!(!(*l).linked);
(*l).linked = true;
(*l).prev = core::ptr::null_mut();
(*l).next = self.head;
if !self.head.is_null() {
(*self.head).link().prev = node;
(*T::link_raw(self.head)).prev = node;
}
}
self.head = node;
}
/// No-op if `node` is not linked (already answered / never dispatched).
pub(crate) fn unlink(&mut self, node: &mut T) {
let node_ptr: *mut T = node;
let l = node.link();
if !l.linked {
return;
}
l.linked = false;
let (prev, next) = (l.prev, l.next);
l.prev = core::ptr::null_mut();
l.next = core::ptr::null_mut();
// SAFETY: neighbours are linked ⇒ arena-live; bundle thread.
pub(crate) fn unlink(&mut self, node: *mut T) {
// SAFETY: `node` is arena-live; its neighbours are linked ⇒ arena-live
// (and still out with the plugins' thread, hence only their links are
// touched); bundle thread.
unsafe {
let l = T::link_raw(node);
if !(*l).linked {
return;
}
(*l).linked = false;
let (prev, next) = ((*l).prev, (*l).next);
(*l).prev = core::ptr::null_mut();
(*l).next = core::ptr::null_mut();
if prev.is_null() {
debug_assert!(core::ptr::eq(self.head, node_ptr));
debug_assert!(core::ptr::eq(self.head, node));
self.head = next;
} else {
(*prev).link().next = next;
(*T::link_raw(prev)).next = next;
}
if !next.is_null() {
(*next).link().prev = prev;
(*T::link_raw(next)).prev = prev;
}
}
}
Expand All @@ -332,8 +341,18 @@ impl<T: OutstandingNode> OutstandingList<T> {
if head.is_null() {
return None;
}
// SAFETY: linked ⇒ arena-live.
self.unlink(unsafe { &mut *head });
self.unlink(head);
Some(head)
}
/// Every linked node, each still out with the plugins: `f` may only touch
/// the fields this side owns, and must not link or unlink.
Comment thread
robobun marked this conversation as resolved.
Outdated
pub(crate) fn for_each(&self, mut f: impl FnMut(*mut T)) {
let mut node = self.head;
while !node.is_null() {
// SAFETY: linked ⇒ arena-live; only the link is read; bundle thread.
let next = unsafe { (*T::link_raw(node)).next };
f(node);
node = next;
}
}
}
Loading