Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cli): refactor bench/test for future module changes #21460

Merged
merged 2 commits into from
Dec 5, 2023

Conversation

mmastrac
Copy link
Contributor

@mmastrac mmastrac commented Dec 4, 2023

Extracting some refactorings for the module work that will land in denoland/deno_core#359

@@ -1,5 +1,9 @@
error: Uncaught TypeError: Cannot read properties of undefined (reading 'fn')
error: TypeError: Cannot read properties of undefined (reading 'fn')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These messages may change as we continue refactoring -- it's a bit annoying that they change because of these refactorings but I'll try to stabilize them with the future work.

Deno.bench();
^
at [WILDCARD]
at [WILDCARD]/bench/no_check.ts:1:6
This error was not caught from a benchmark and caused the bench runner to fail on the referenced module.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes the benchmark and test runner more consistent

Copy link
Member

Choose a reason for hiding this comment

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

Very nice 👍

cli/tools/test/mod.rs Outdated Show resolved Hide resolved
receiver.await
}
}
}

/// Run the event loop up to a given duration.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I might promote this to deno_core but I would like to let it bake in deno for a bit

Deno.bench();
^
at [WILDCARD]
at [WILDCARD]/bench/no_check.ts:1:6
This error was not caught from a benchmark and caused the bench runner to fail on the referenced module.
Copy link
Member

Choose a reason for hiding this comment

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

Very nice 👍

@@ -180,6 +214,10 @@ async fn bench_specifier(
worker.execute_side_module_possibly_with_npm().await?;

let mut worker = worker.into_main_worker();

// Ensure that there are no pending exceptions before we start running tests
worker.run_up_to_duration(Duration::from_millis(0)).await?;
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this racy? Ie. won't in some situations timeouts fire before event loop tick finishes?

Copy link
Contributor Author

@mmastrac mmastrac Dec 5, 2023

Choose a reason for hiding this comment

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

This turns out to catch 100% of the existing promise resolution failures we have today, so I left it at the minimum value that works. In the current system promise rejections are loaded only if they are raised in the same tick as a module resolution which is extremely brittle.

Comment on lines +494 to +496
// Ensure the worker has settled so we can catch any remaining unhandled rejections. We don't
// want to wait forever here.
worker.run_up_to_duration(Duration::from_millis(0)).await?;
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit concerning to me, why unhandled rejections are not caught by the previous line that dispatch the unload event? Does that mean that we need to update all callsites in the whole codebase to now catch these rejections properly?

Copy link
Contributor Author

@mmastrac mmastrac Dec 5, 2023

Choose a reason for hiding this comment

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

I am currently separating out the functionality of our "polling" event-loop driving code (ie: poll_event_loop) and our "immediate" event-loop driving code (call_and_await, resolve_promise). Unhandled promise rejections are a concern of the "polling" mode of operation, as any number of async tasks may be raised by immediate calls.

Most of this code works only by luck, and only for rejections handled before the function returns. For us to ensure that all rejections pass through the same common unhandled rejection paths, we need to process rejections outside of all immediate APIs and process them entirely through the "polling" interface.

After investigation, it turns out that bench and test are the only places in Deno where we drive the event loop in immediate mode.

Comment on lines +563 to +566
/// Run the event loop up to a given duration. If the runtime resolves early, returns
/// early. Will always poll the runtime at least once.
pub async fn run_up_to_duration(
&mut self,
Copy link
Member

Choose a reason for hiding this comment

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

Cool, we should eventually upstream it to deno_core 👍

@mmastrac mmastrac merged commit 4a9f429 into denoland:main Dec 5, 2023
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants