-
Notifications
You must be signed in to change notification settings - Fork 246
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
fix(runtime): "Error: EOF: end of file, read" on Windows #2238
Conversation
NodeJS makes it unsafe to perform syncrhonous (aka blocking) operations on file descriptors 0 through 2 (STDIN, STDOUT, STDERR), as those are set to `O_NONBLOCK` by `libuv`. This is particularly problematic on Windows where there is no easy way to re-open those in blocking mode as can be done on most UNIX platforms today using `/dev/std{in,out,err}` pseudo-files. Consequently, we must handle some quirks in cases where blocking attempts result in conditions typical of non-blocking access: `EAGAIN` errors must be caught and retried, and `EOF` errors must be "ignored". This PR adds the necessary code to correctly handle the `EOF` error, and adds a 50ms sleep before re-trying a `EAGAIN` error that is achieved thanks to a neat trick suggested by the [`sleep` npm package][sleep]: using `Atomics.wait` enables syncrhonous sleep to be done in `node` without having to ship a native module. Additionally, this PRs re-opens `STDIN`, `STDOUT` and `STDERR` when the `/dev/std{in,out,err}` files are present, reducing the incidence of this problem on most UNIX platfoms. [sleep]: https://www.npmjs.com/package/sleep
c4b7c50
to
221edff
Compare
@@ -1,3 +1,5 @@ | |||
import * as process from 'process'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added as part of my rumblings here... process.env
is used here, and I kinda like to standardize on importing this even though it's implicit.
@@ -217,6 +217,7 @@ jobs: | |||
python: '3.6' | |||
# Test alternate .NETs | |||
- java: '8' | |||
# Current release info can be found at https://dotnet.microsoft.com/download/dotnet/5.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not strictly related to this PR, but this can be helpful in the future. This is kinda trivial, too. This got added as I was trying to find a way to reliably cause an EOF to happen on Windows in CI/CD. Spoiler: I did not succeed, for some reason.
// eslint-disable-next-line no-constant-condition | ||
while (true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used a while
loop instead of recursively re-entering, as this avoids stacking up in case EAGAIN
happens a lot, which I imagine might be somewhat frequent on resource-constrained systems (where we'd likely also have less memory available for wasting on stack frames).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a nice comment to have in source, rather than just on the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of my comments are about comments. :)
Also, are there any tests for this section of the code?
// force a O_SYNC access to STDIN in a reliable way within node. | ||
// In order to make this stop we need to either stop depending on synchronous reads, or to | ||
// provision our own communication channel that can reliably be synchronous. This work is | ||
// "tracked" at https://github.com/aws/aws-cdk/issues/5187 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this was copy/pasted; is it still relevant? The referenced issue is closed. Do we have an active/open issue, and/or jsii
issue to "track" this?
|
||
const EMPTY_BUFFER = Buffer.alloc(0); | ||
const STDIN_FD = (process.stdin as any).fd ?? 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When will these (not) be set? Might deserve a comment.
packages/@jsii/runtime/lib/sleep.ts
Outdated
const array = new Int32Array(new SharedArrayBuffer(4)); | ||
|
||
/** | ||
* Sleeps for a set amount of time, syncrhonously. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Sleeps for a set amount of time, syncrhonously. | |
* Sleeps for a set amount of time, synchronously. |
// eslint-disable-next-line no-constant-condition | ||
while (true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a nice comment to have in source, rather than just on the PR.
Thank you for contributing! ❤️ I will now look into making sure the PR is up-to-date, then proceed to try and merge it! |
Merging (with squash)... |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Merging (with squash)... |
NodeJS makes it unsafe to perform syncrhonous (aka blocking) operations
on file descriptors 0 through 2 (STDIN, STDOUT, STDERR), as those are
set to
O_NONBLOCK
bylibuv
. This is particularly problematic onWindows where there is no easy way to re-open those in blocking mode.
Consequently, we must handle some quirks in cases where blocking
attempts result in conditions typical of non-blocking access:
EAGAIN
errors must be caught and retried, and
EOF
errors must be "ignored".This PR adds the necessary code to correctly handle the
EOF
error, andadds a 50ms sleep before re-trying a
EAGAIN
error that is achievedthanks to a neat trick suggested by the
sleep
npm package:using
Atomics.wait
enables syncrhonous sleep to be done innode
without having to ship a native module.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.