It was a bit confusing having two different API libraries. Instead of
opening `Fibreslib`, it is now suggested to open `Eio.Std`, which
exports fewer things.
Process completed events using `Uring.peek` before submitting.
Otherwise, we call submit once for every operation, defeating the
purpose of uring.
Also, slightly optimise `Eunix.free`.
Using `wrk -c 1000 -d 5s`, this increases performance from 277.30MB/s to
414.12MB/s.
This allows attaching resources, such as file descriptors, to switches.
When the switch is finished, you can be sure the resources have been
freed.
This also fixes some FD leaks in the tests and README that this uncovered.
`io_uring`'s rules seem to be:
- For files, we must pass -1 to use the current offset
- For pipes, we can pass either -1 or 0
- For sockets, we must pass 0
If multiple threads fail, report all the exceptions. OCaml doesn't
provide a way to add context exceptions, so we have to introduce a new
exception type for this.
Also, `Switch.check` now wraps the exception in `Cancelled`. This allows
a thread to distinguish e.g. an `End_of_file` in its own operations
from being cancelled due to another thread hitting `End_of_file`.
Finally, `Fibre.fork_ignore ~sw` now copes with `sw` being off at the
start. It continues execution in the main thread, but does not run the
child. This is similar to the behaviour when `sw` is turned off
immediately after forking.
Previously, the scheduler returned whenever there were no pending
events. This could be because the main function had finished, but it
could also happen if the system was deadlocked.
Now, the scheduler raises an exception if it finishes while the main
thread is still running, which should make the problem clearer.