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

EAGAIN returned by fs.read #2663

Closed
hbr opened this issue Apr 30, 2020 · 7 comments
Closed

EAGAIN returned by fs.read #2663

hbr opened this issue Apr 30, 2020 · 7 comments

Comments

@hbr
Copy link

hbr commented Apr 30, 2020

  • Node.js Version: v13.12.0
  • OS: OSX
  • Scope (install, code, runtime, meta, other?): code
  • Module (and version) (if relevant): fs

Under what circumstances can fs.read(....) call the callback with the error code EAGAIN. I was thinking this can never be the case, because fs.read is asynchronous i.e. non blocking.

If I try the following code snippet (reading from stdin)

const fs = require('fs')

const buffer = Buffer.alloc (200)

fs.read (0, buffer, 0, 200, null, (err,nbytes,buffer) => {
    console.log (nbytes + ' bytes read')
})

it works fine.

But I have the same code embedded in some more complex setting (the javascript code is generated by js_of_ocaml), I get under some circumstances an error with EAGAIN when reading from stdin i.e. 0 which is connected to a terminal.

Am I missing some important details, because from my understanding, this behaviour should never happen.

@bnoordhuis
Copy link
Member

If the file descriptor that you pass to fs.read() is in non-blocking mode, then the read(2) will fail with EAGAIN if no data is available. That's expected behavior and it sounds like that is what is happening here.

@hbr
Copy link
Author

hbr commented May 1, 2020

Thanks for the answer.

How is it possible in node to make a file descriptor non blocking. The filedescriptor is 0 i.e. stdin and it is connected to the terminal. Since I do not change the file descriptor mode, it should be blocking and the program should perform as my code snippet above.

However this command fails immediately with EAGAIN

node my_program

while

node my_program <some_file

does its job without EAGAIN.

The only job of the program is to read something from stdin, process it and return the output to stdout.

There is another strange thing I observed. If I hack the file descriptor and explicitly put 1 instead of 0, then the first command succeeds as expected, while the second command fails.

@bnoordhuis
Copy link
Member

Node controls fd 0-2. I.e., your script can read from or write to them directly. Use e.g. process.stdin.on('data', cb) (or a promisified version) instead.

W.r.t. your examples: node decides based on the type of the stdio file descriptor whether to put it in non-blocking mode or not and how to read from it. When stdin is an on-disk file, it uses fs.read(); when it's a tty, it uses something else.

@hbr
Copy link
Author

hbr commented May 3, 2020

I still do not understand whats going on here.

The main question: Why is the file descriptor 0 in non-blocking mode?

A have read and reread the node documentation for fs. If I open a disk file, I can put the corresponding file descriptor into non-blocking mode. However I have found no possibility to explicitly put fd 0-2 into non-blocking mode. Obviously I might have used some node functions which put fd 0 into non-blocking mode as a side effect.

Initially fd 0-2 are in blocking mode. My simple code snippet above proves this for stdin. This should be the case, regardless if the file descriptors are connected to a disk file or a tty.

Clearly, nobody can help me to find the part of my code which accidentially has put stdin into non-blocking mode. But I hope to find some hints, which actions could possibly cause that.

If I'm using the wrong interface, I certainly can switch to the stream based interface. But since fs.read() is marked as long term stable, I am a little bit reluctant to switch to another interface before I have understood why the way I use it is inappropriate.

Since I want to use node very heavily, it is important for me to understand correctly the semantics of basic functions like fs.read().

Thanks for any hints

  • Helmut

@bnoordhuis
Copy link
Member

The main question: Why is the file descriptor 0 in non-blocking mode?

Sorry if I wasn't clear: node puts it in non-blocking mode in order that process.stdin.pipe(...) or process.stdin.on('data', ...) works. Higher level: in order that e.g. the REPL can integrate with the event loop.

The difference you observe with different types of stdin is due to the fact that tty file descriptors integrate with kqueue(2) (what powers node's event loop on macos) but file descriptors that refer to on-disk files don't.

process.stdin abstracts that away - it'll do the right thing depending on the type of file descriptor.

@hbr
Copy link
Author

hbr commented May 5, 2020

Thanks Ben for the comment.

Now I begin to understand, what's going on here.

The only remaining question: What is the condition for node putting stdin connected to a tty into non-blocking mode?

For me it seems to be sporadic. In the code snippet in my_file.js

const fs = require('fs')

const buffer = Buffer.alloc (200)

fs.read (0, buffer, 0, 200, null, (err,nbytes,buffer) => {
    console.log (nbytes + ' bytes read')
})

The command node my_file.js does not put stdin into non blocking mode. The program waits until it encounters some input as long as I don't press ctrl-D. The same code snippet, embedded in my program does not wait for input. It just fails with EAGAIN.

Do you have any idea what could be the difference?

Thanks.

@bnoordhuis
Copy link
Member

Anything accessing stdio through the "ordained" APIs can put it in non-blocking mode. In theory a console.log() is enough although in practice it's probably something like repl.start().

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

No branches or pull requests

2 participants