Repository: node
Issue: Worker with connected debugger hangs the process
Labels: inspector,worker

<!--
Thank you for reporting an issue.

This issue tracker is for bugs and issues found within Node.js core.
If you require more general support please file an issue on our help
repo. https://github.com/nodejs/help


Please fill in as much of the template below as you're able.

Version: output of `node -v`
Platform: output of `uname -a` (UNIX), or output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in PowerShell console (Windows)
Subsystem: if known, please specify affected core module name
-->

* **Version**: v15.11.0
* **Platform**: macOS 11.0.1 (Darwin sebastian-desktop 20.1.0 Darwin Kernel Version 20.1.0: Sat Oct 31 00:07:11 PDT 2020; root:xnu-7195.50.7~2/RELEASE_X86_64 x86_64)
* **Subsystem**: N/A

Referred from Twitter thread https://twitter.com/addaleax/status/1367895218624823299

### What steps will reproduce the bug?

Run the following script:

```js
const {Worker} = require("worker_threads");
const WebSocketClient = require("websocket").client;
const http = require("http");

const worker = new Worker(`
  const {parentPort} = require("worker_threads");
  const inspector = require("inspector");
  inspector.open(8888);

  // Inspector URLs have a UID path so it needs to be explicitly sent
  parentPort.postMessage(inspector.url());

  // Create a ref to keep the process alive
  setInterval(() => {}, 1000);
`, {
  eval: true,
});

worker.on("message", (url) => {
  const client = new WebSocketClient();

  client.on("connect", function(connection) {
    // This is a requirement
    connection.sendUTF(JSON.stringify({
      id: 1,
      method: "Debugger.enable",
    }));

    // Give the inspector enough time to handle the debugger enable message
    setTimeout(() => {
      process.exit();
      // or
      worker.terminate();
    }, 1000);
  });
  
  client.on("connectFailed", function(err) {
    console.log(err);
  });

  client.connect(url);
});
```

This requires the `websocket` module to create the debugger connection, but does not depend on it.

### How often does it reproduce? Is there a required condition?

A worker needs to be created, with a debugger connected from the parent process. When the parent process decides it's done and wants to quit, the worker could be told to exit in two ways:

**Calling `worker.terminate()`** The thread is never ended. This makes sense as if a regular Node process is exited with a connected debugger then it will log "Waiting for the debugger to disconnect" and wait. This message isn't displayed for workers so it wasn't immediately obvious. I don't know of any optimal solution for this case.

**Calling `process.exit()` in the parent** If the connected debugger socket is owned by the process then the entire process is deadlocked. If `NODE_DEBUG=worker` is set, there is no "terminates Worker" log, as you would get from explicitly calling `worker.terminate()`.

### What is the expected behavior?

<!--
If possible please provide textual output instead of screenshots.
-->

**Calling `worker.terminate()`** I think the current behavior is acceptable. My use-case is niche. I assume that the "waiting for the debugger" message was suppressed in workers for a reason.

**Calling `process.exit()` in the parent** The socket should be destroyed, and thus there should be no debugger connections for the thread to wait on.

### What do you see instead?

<!--
If possible please provide textual output instead of screenshots.
-->

The process hangs.

### Additional information

<!--
Tell us anything else you think we should know.
-->

This usage is extracted from [Rome](https://github.com/rome/tools) where we have debugger connections setup for test workers. We use the debugger to dump the current stack trace for timeouts.