I'm trying to narrow down the cause of this issue and have run into some behaviour I didn't expect.
I'm running Node v11.8.0 on Ubuntu 18.04.1 LTS.
If I run this script:
const fs = require('fs');
const { promisify } = require('util');
const { spawn, spawnSync, execSync } = require('child_process');
const { O_RDWR } = fs.constants;
const pipePath = "/tmp/myPipe";
let pipeHandle;
let stream;
function pipeListener(data) {
console.log("Pipe data received", data.length);
}
async function setupPipe() {
await promisify(fs.unlink)(pipePath)
.then(function() {}, function(err) {console.warn("Error removing pipe", err);}); // Ignore errors which may happen if the pipe doesn't exist
spawnSync('mkfifo', [pipePath]);
pipeHandle = await promisify(fs.open)(pipePath, O_RDWR); // https://stackoverflow.com/a/53492514/674675
stream = fs.createReadStream(null, {fd: pipeHandle, autoClose: false});
stream.on('data', pipeListener);
stream.on('close', () => {/*console.log("Stream closed");*/});
stream.once('data', () => {
console.log("First data");
});
}
async function teardownPipe() {
stream.removeAllListeners('data');
stream.close();
}
async function reproduceBug() {
let i = 0;
while(i < 50) {
try {
let stat = fs.fstatSync(pipeHandle);
let path = fs.readlinkSync('/proc/self/fd/' + pipeHandle);
console.warn("File", path, "still open after iteration", i);
} catch (ex) {
}
i++;
await setupPipe();
await teardownPipe();
}
setInterval(() => {
// Prevents the process from terminating so that you can inspect the handles with lsof
}, 1000);
}
reproduceBug().then(() => {
console.log("Done");
}, (err) => {
console.error(err);
});
I would expect to see consistent results from each iteration of the loop.
But instead, I'm seeing inconsistent and unpredictable results:
josh@desktop-01:~$ node ./pipeMre.js
File /tmp/myPipe still open after iteration 9
File /tmp/myPipe still open after iteration 13
File /tmp/myPipe still open after iteration 14
File /tmp/myPipe still open after iteration 27
Done
josh@desktop-01:~$ node ./pipeMre.js
File /tmp/myPipe still open after iteration 7
File /tmp/myPipe still open after iteration 11
File /tmp/myPipe still open after iteration 14
Done
^C
josh@desktop-01:~$ node ./pipeMre.js
File /tmp/myPipe still open after iteration 8
File /tmp/myPipe still open after iteration 29
File /tmp/myPipe still open after iteration 43
File /tmp/myPipe still open after iteration 46
Done
^C
josh@desktop-01:~$ node ./pipeMre.js
File /tmp/myPipe still open after iteration 6
File /tmp/myPipe still open after iteration 7
File /tmp/myPipe still open after iteration 21
File /tmp/myPipe still open after iteration 40
File /tmp/myPipe still open after iteration 48
Done
Is this meant to happen? Why is the file descriptor sometimes closed and other times not? In my full application some of the file descriptors stay open until the process is terminated, which I'm having trouble reproducing in my minimal example.