cluster: centralize removal from workers list.

Currently, cluster workers can be removed from the workers list in three
different places:
- In the exit event handler for the worker process.
- In the disconnect event handler of the worker process.
- In the disconnect event handler of the cluster master.

However, handles for a given worker are cleaned up only in one of these
places: in the cluster master's disconnect event handler.

Because these events happen asynchronously, it is possible that the
workers list is empty before we even clean up one handle. This makes
the assert that makes sure that no handle is left when the workers
list is empty fail.

We could either remove the associated handles every time we remove a
worker from the list, or remove workers from the list in only one
handler. This commit implements the latter.

Fixes #8191 and #8192.
Initializing `process.exitCode` to `0` here doesn't seem to be consistent with how it's used elsewhere in the code. It means that `worker.process.exitCode` is `0` in a worker when it starts executing, although it has not exited yet.

The convention in the rest of the code seems to be that `process.exitCode` is `undefined` until the process has exited.

With this change, all tests pass, but I understand it could have a big impact, so I'd rather highlight it now so that we consider it carefully.