Admin message

Due to a large amount of spam we do not allow new users to create repositories, they are "external" users. If you are a new user and want to create a repository, for example for forking GHC, open a new issue on ghc/ghc using the "get-verified" issue template

GHC.Event.Thread.closeFdWith isn't concurrency-safe
While looking at some preparatory work for !8073 I noticed that `GHC.Event.Thread.closeFdWith` is extremely suspicious. Specifically, it: 1. iterates over the capabilities and takes the event callback table `MVar` of each 2. iterates over the capabilities and calls `GHC.Event.Manager.closeFd_` on each previously-taken table to remove the to-be-closed fd 3. closes the fd 4. iterates over the capabilities and for each: * puts the event callback table `MVar` * runs the event notifier registered for the closed fd, if any All of this happens under an `uninterruptibleMask_`. First, a bit of background: `closeFdWith` is needed because POSIX does not specify the behavior of `select` or `poll` when an `fd` being blocked on in one thread is closed by another thread. Consequently, the IO manager must (atomically) ensure that all events registered against the fd are dispatched before the `fd` is closed. However, AFAICT, the above implementation can very easily break if the number of capabilities is concurrently changed. Specifically I can imagine the following problematic interleaving: ``` │ Capability 1 │ Capability 2 ──────────── │ ──────────── │ 0. `closeFdWith fd` is called │ │ 1. Takes event callback tables for all N │ capabilities │ │ │ 2. Calls `setNumCapabilities (N+1)`, blocks │ waiting to acquire all capabilities │ 3. Yields its capability (e.g. due to GC) │ │ │ 3. Takes capability, and now holds all capabilities. │ A new capability is spun-up and all capabilities │ are again released. │ │ 4. A thread is scheduled to capability N+1 │ │ 5. The thread registers an event for `fd` and blocks on `select`/`poll` │ 6. `closeFdWith` continues, calling │ `closeFd_` on N capabilities. │ This removes the fd from the event │ list of all capabilities and │ wakes up those that are blocked │ on the fd │ │ 7. the fd is closed │ │ │ 8. The thread is now blocked on `fd`, which was closed while │ the thread was blocked, triggering undefined behavior. │ ``` This is quite problematic.
issue