Repository: node
Issue: Passing fd between threads causes VM to crash, or EBADF 
Labels: worker

<!--
Thank you for reporting a possible bug in Node.js.

Please fill in as much of the template below as you can.

Version: output of `node -v`
Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
Subsystem: if known, please specify the affected core module name

If possible, please provide code that demonstrates the problem, keeping it as
simple and free of external dependencies as you can.
-->

* **Version**: 12.13.0
* **Platform**: Linux x 5.0.0-36-generic #39-Ubuntu SMP Tue Nov 12 09:46:06 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
* **Subsystem**: net, worker_threads

<!-- Please provide more details below this comment. -->
## From forks to threads
I was working on moving from forks to Worker Threads and was experimenting with passing file descriptors between threads. Forks work great with passing file descriptors around due to the extended IPC, making it possible to share open ports. 

## Sharing file descriptors
Using Worker Threads, this becomes impossible, For that reason, I had to create a master thread handling connection events, and pass those connections to worker threads through `postMessage`. Since the whole object cannot be passed through message, I thought the lightest and fastest way to do this would be to post the fd as a message, and have the worker thread create a new `Socket` using the fd.

It works great until it does not. It is really unpredictable, but always seems to crash at some point. 

## Repro steps
Here is the smallest portion of code I could come up with to reproduce the issue. Those are two files : **master.js** and **worker.js**.

#### Full test **[HERE](https://github.com/rykdesjardins/nodejs_fd_passing_testing)**.

```js
// master.js
const { Worker } = require('worker_threads');
const net = require('net');

const workers = new Worker("./worker.js");

const server = net.createServer(conn => {
    conn.unref();
    workers.postMessage({ duplex_fd : conn._handle.fd });
});

server.listen(12345);
```

```js
// worker.js
const { Socket } = require('net');

require('worker_threads').parentPort.on('message', (msg) => {
    const sock = new Socket({ fd : msg.duplex_fd, readable : true, writable : true, allowHalfOpen : true });  
    sock.end("Hello, World", () => {
        sock.destroy();
    });
})
```

After _an unpredictable while_, I get either one of those two errors : 
```
node: ../deps/uv/src/unix/core.c:930: uv__io_stop: Assertion `loop->watchers[w->fd] == w' failed.
Aborted (core dumped)
```

or

```
events.js:187
      throw er; // Unhandled 'error' event
      ^

Error: read EBADF
    at TCP.onStreamRead (internal/stream_base_commons.js:201:27)
Emitted 'error' event on Socket instance at:
    at emitErrorNT (internal/streams/destroy.js:92:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
    at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  errno: 'EBADF',
  code: 'EBADF',
  syscall: 'read'
}
```

This is something I used to do in C++ : have a master thread handle incoming connections, and pass the `fd` integer to whatever thread is available. Maybe I'm misunderstanding how Nodejs handles file descriptors in the background? 

The full example I wrote had a worker pool and sometimes was able to handle over 5000 requests before crashing. The crashes are random. 

If it can help, here is the `stress.js` file I used to conduct the tests.
```js
// stress.js
const net = require('net');
const cluster = require('cluster');

if (cluster.isMaster) {
    let reqSent = 0;
    for (let i = 0; i < 10; i++) cluster.fork().on('message', m => m == "+" && console.log(reqSent++));
} else {
    const sendReq = () => {
        const sock = net.connect(12345, 'localhost', () => {
            sock.write("Hello?", () => {
                sock.end();
                sock.destroy();
                process.send("+");
                setImmediate(() => sendReq());
            });
        });
    };
    sendReq();
}
```

Let me know if you need more info, or if I simply misunderstand how to use this feature.

### Notes
This also happens with file streams, and sockets on top of HTTP.