Skip to content
Merged
Show file tree
Hide file tree
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
37 changes: 36 additions & 1 deletion src/runtime/bake/DevServer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ pub enum TestingBatchEvents {
/// a message saying that new files have been seen. Once DevServer receives
/// that signal, or times out, it will "release" this batch.
Enabled(TestingBatch),
/// Released while a bundle ran; `finalize_bundle_cleanup` starts it after.
ReleaseAfterBundle(TestingBatch),
}

/// There is only ever one bundle executing at the same time, since all bundles
Expand Down Expand Up @@ -1151,7 +1153,9 @@ impl Drop for DevServer {
}
}

if let TestingBatchEvents::Enabled(batch) = &mut self.testing_batch_events {
if let TestingBatchEvents::Enabled(batch) | TestingBatchEvents::ReleaseAfterBundle(batch) =
&mut self.testing_batch_events
{
drop(std::mem::replace(
&mut batch.entry_points,
EntryPointList::empty(),
Expand Down Expand Up @@ -3778,6 +3782,20 @@ fn finalize_bundle_cleanup(dev: &mut DevServer, bv2: &mut BundleV2, had_sent_hmr

dev.start_next_bundle_if_present();

// If the call above started another bundle, its cleanup releases the batch.
if matches!(
dev.testing_batch_events,
TestingBatchEvents::ReleaseAfterBundle(_)
) && dev.current_bundle.is_none()
{
let TestingBatchEvents::ReleaseAfterBundle(batch) =
core::mem::replace(&mut dev.testing_batch_events, TestingBatchEvents::Disabled)
else {
unreachable!()
};
dev.release_testing_batch(batch);
}

// Unref the ref added in `start_async_bundle`
if let Some(server) = dev.server.as_mut() {
server.on_static_request_complete();
Expand Down Expand Up @@ -4873,6 +4891,23 @@ pub(super) fn finalize_bundle(
}

impl DevServer {
/// Bundle the files a testing batch collected, or report an empty batch.
pub(crate) fn release_testing_batch(&mut self, batch: TestingBatch) {
debug_assert!(self.current_bundle.is_none());
if batch.entry_points.set.count() == 0 {
self.publish(
HmrTopic::TestingWatchSynchronization,
&[MessageId::TestingWatchSynchronization.char(), 2],
Opcode::BINARY,
);
return;
}

self.start_async_bundle(batch.entry_points, true, Instant::now())
// bun.handleOom(err) — Rust aborts on OOM by default
.expect("OOM");
}

fn start_next_bundle_if_present(&mut self) {
debug_assert!(self.magic == Magic::Valid);
// Clear the current bundle
Expand Down
33 changes: 15 additions & 18 deletions src/runtime/bake/dev_server/hmr_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ impl HmrSocket {
}
x if x == IncomingMessageId::SetUrl as u8 => {
let pattern = &msg[1..];
// `match_slow` requires an absolute path; these are peer bytes.
if pattern.first() != Some(&b'/') {
return ws.close();
}
// SAFETY: JS-thread only; sole `&mut DevServer` for this scope.
let dev = unsafe { self.dev() };
let maybe_rbi = dev.route_to_bundle_index_slow(pattern);
Expand Down Expand Up @@ -204,36 +208,29 @@ impl HmrSocket {
);
}
}
super::TestingBatchEvents::EnableAfterBundle => {
// do not expose a websocket event that panics a release build
debug_assert!(false);
super::TestingBatchEvents::EnableAfterBundle
| super::TestingBatchEvents::ReleaseAfterBundle(_) => {
// A duplicate `H` is a protocol violation, not an invariant.
ws.close();
}
super::TestingBatchEvents::Enabled(_event_const) => {
Comment thread
robobun marked this conversation as resolved.
// Replace-and-extract to satisfy borrowck.
let super::TestingBatchEvents::Enabled(mut event) = core::mem::replace(
let super::TestingBatchEvents::Enabled(batch) = core::mem::replace(
&mut dev.testing_batch_events,
super::TestingBatchEvents::Disabled,
) else {
unreachable!()
};
let _ = &mut event;

if event.entry_points.set.count() == 0 {
dev.publish(
HmrTopic::TestingWatchSynchronization,
&[MessageId::TestingWatchSynchronization.char(), 2],
bun_uws::Opcode::BINARY,
);
// An unbundled route's request can start a bundle;
// `start_async_bundle` requires none in flight.
Comment thread
robobun marked this conversation as resolved.
if dev.current_bundle.is_some() {
dev.testing_batch_events =
super::TestingBatchEvents::ReleaseAfterBundle(batch);
return;
}

let timer = std::time::Instant::now();
dev.start_async_bundle(event.entry_points, true, timer)
// bun.handleOom(err) — Rust aborts on OOM by default
.expect("OOM");

// `event.entry_points.deinit(allocator)` → Drop handles this
dev.release_testing_batch(batch);
}
}
}
Expand Down Expand Up @@ -307,7 +304,7 @@ impl HmrSocket {
}
if field.contains(HmrTopic::MemoryVisualizer.as_bit()) {
dev.emit_memory_visualizer_events -= 1;
if dev.emit_incremental_visualizer_events == 0
if dev.emit_memory_visualizer_events == 0
&& dev.memory_visualizer_timer.state == EventLoopTimerState::ACTIVE
{
// Note (jsc/runtime crate cycle): `vm.timer` is `()` on the low-tier
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/bake/dev_server/memory_cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ pub(crate) fn memory_cost_detailed(dev: &DevServer) -> MemoryCost {
// .testing_batch_events
match &dev.testing_batch_events {
TestingBatchEvents::Disabled => {}
TestingBatchEvents::Enabled(batch) => {
TestingBatchEvents::Enabled(batch) | TestingBatchEvents::ReleaseAfterBundle(batch) => {
other_bytes += memory_cost_array_hash_map(&batch.entry_points.set);
}
TestingBatchEvents::EnableAfterBundle => {}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/bake/dev_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ impl HotReloadEvent {

match &mut dev_ref.testing_batch_events {
TestingBatchEvents::Disabled => {}
TestingBatchEvents::Enabled(ev) => {
TestingBatchEvents::Enabled(ev) | TestingBatchEvents::ReleaseAfterBundle(ev) => {
bun_core::handle_oom(ev.append(&entry_points));
dev_ref.publish(
HmrTopic::TestingWatchSynchronization,
Expand Down
Loading
Loading