Eio_linux: handle sleeping tasks that are now due before paused fibers#213
Eio_linux: handle sleeping tasks that are now due before paused fibers#213talex5 merged 2 commits intoocaml-multicore:mainfrom
Conversation
|
I'm not familiar with the code so excuse me if I'm asking something stupid. |
|
This does need fixing, but I think we need a more general solution. A fiber that keeps yielding will also prevent any other IO from happening, not just sleeps. Perhaps we should keep a "check-for-IO" job on the run queue. When it gets to the head, we dispatch any IO (and add another check-for-IO job)? |
|
Thanks for your reviews ! Yes indeed I should have warned that this was written on top of my head to fix the specific issue and start a discussion.
That's what I was thinking, I can try to implement that. |
6e43b55 to
23997a8
Compare
|
@talex5 I have updated the PR. Please take a look and tell me if something is wrong. |
talex5
left a comment
There was a problem hiding this comment.
Thanks; this looks generally sensible!
But with this change, make test_luv now hangs, so we probably need a similar fix there.
Other notes:
-
I think it would be simpler to delay adding the IO event until just before returning. Then you don't need
has_single_element. I had a go at that here: https://github.com/ocaml-multicore/eio/compare/main...talex5:fairness?expand=1 -
The fake test clock in
prelude.mlsays:At the moment, the scheduler only checks for expired timers when the run-queue is
empty, so this is a convenient way to wait for the system to be idle.It still seems to be working, but might need to make it more robust, or at least update the comment to warn that it might not work now.
|
Thank you for your review.
I have added a fix to the luv backend in the second commit. In particular I'm using
Indeed that sounds better and reduces the diff. I have force-pushed my first commit with the suggested changes
Do you have an idea on how to make it more robust ? Do you have tests that are waiting for the run queue to become empty ? |
This has a large effect on the benchmarks! e.g. Before: After: io-uring is also affected, though not as much. After: |
|
Thank you for performing the benchmarks. I expected a performance cost but not that high for the luv backend. A single task yielding is indeed the worst case, the run queue having 1 task and and 1 To address the performance cost globally, what we can do is remove the
This strategy has a negligible runtime cost when no busy yielding is happening. What do you think ? About the luv backend, I'm not familiar enough with it to figure out how to improve the performances on that side. |
talex5
left a comment
There was a problem hiding this comment.
Thinking on about this, I don't think the performance change for Eio_linux is a problem: the yield benchmark previously wasn't checking for IO, but now it is, and that's correct. It's not surprising it's slightly slower.
I tested the luv changes with the HTTP benchmark (https://github.com/ocaml-multicore/retro-httpaf-bench, adjusted to use Eio_luv) and this PR didn't make any noticeable difference, so I think it's fine.
However, the tests are failing in CI (but they work for me locally). Any idea why that is?
|
I have no idea why the CI failure is happening. I have also been able to reproduce the test using the Docker instructions of the CI job.. |
|
Thanks! |
CHANGES: API changes: - `Net.accept_sub` is deprecated in favour of `accept_fork` (@talex5 ocaml-multicore/eio#240). `Fiber.fork_on_accept`, which it used internally, has been removed. - Allow short writes in `Read_source_buffer` (@talex5 ocaml-multicore/eio#239). The reader is no longer required to consume all the data in one go. Also, add `Linux_eio.Low_level.writev_single` to expose this behaviour directly. - `Eio.Unix_perm` is now `Eio.Dir.Unix_perm`. New features: - Add `Eio.Mutex` (@TheLortex @talex5 ocaml-multicore/eio#223). - Add `Eio.Buf_write` (@talex5 ocaml-multicore/eio#235). This is a buffered writer for Eio sinks, based on Faraday. - Add `Eio_mock` library for testing (@talex5 ocaml-multicore/eio#228). At the moment it has mock flows and networks. - Add `Eio_mock.Backend` (@talex5 ocaml-multicore/eio#237 ocaml-multicore/eio#238). Allows running tests without needing a dependency on eio_main. Also, as it is single-threaded, it can detect deadlocks in test code instead of just hanging. - Add `Buf_read.{of_buffer, of_string, parse_string{,_exn}, return}` (@talex5 ocaml-multicore/eio#225). - Add `<*>` combinator to `Buf_read.Syntax` (@talex5 ocaml-multicore/eio#227). - Add `Eio.Dir.read_dir` (@patricoferris @talex5 ocaml-multicore/eio#207 ocaml-multicore/eio#218 ocaml-multicore/eio#219) Performance: - Add `Buf_read` benchmark and optimise it a bit (@talex5 ocaml-multicore/eio#230). - Inline `Buf_read.consume` to improve performance (@talex5 ocaml-multicore/eio#232). Bug fixes / minor changes: - Allow IO to happen even if a fiber keeps yielding (@TheLortex @talex5 ocaml-multicore/eio#213). - Fallback for `traceln` without an effect handler (@talex5 ocaml-multicore/eio#226). `traceln` now works outside of an event loop too. - Check for cancellation when creating a non-protected child context (@talex5 ocaml-multicore/eio#222). - eio_linux: handle EINTR when calling `getrandom` (@bikallem ocaml-multicore/eio#212). - Update to cmdliner.1.1.0 (@talex5 ocaml-multicore/eio#190).
CHANGES: API changes: - `Net.accept_sub` is deprecated in favour of `accept_fork` (@talex5 ocaml-multicore/eio#240). `Fiber.fork_on_accept`, which it used internally, has been removed. - Allow short writes in `Read_source_buffer` (@talex5 ocaml-multicore/eio#239). The reader is no longer required to consume all the data in one go. Also, add `Linux_eio.Low_level.writev_single` to expose this behaviour directly. - `Eio.Unix_perm` is now `Eio.Dir.Unix_perm`. New features: - Add `Eio.Mutex` (@TheLortex @talex5 ocaml-multicore/eio#223). - Add `Eio.Buf_write` (@talex5 ocaml-multicore/eio#235). This is a buffered writer for Eio sinks, based on Faraday. - Add `Eio_mock` library for testing (@talex5 ocaml-multicore/eio#228). At the moment it has mock flows and networks. - Add `Eio_mock.Backend` (@talex5 ocaml-multicore/eio#237 ocaml-multicore/eio#238). Allows running tests without needing a dependency on eio_main. Also, as it is single-threaded, it can detect deadlocks in test code instead of just hanging. - Add `Buf_read.{of_buffer, of_string, parse_string{,_exn}, return}` (@talex5 ocaml-multicore/eio#225). - Add `<*>` combinator to `Buf_read.Syntax` (@talex5 ocaml-multicore/eio#227). - Add `Eio.Dir.read_dir` (@patricoferris @talex5 ocaml-multicore/eio#207 ocaml-multicore/eio#218 ocaml-multicore/eio#219) Performance: - Add `Buf_read` benchmark and optimise it a bit (@talex5 ocaml-multicore/eio#230). - Inline `Buf_read.consume` to improve performance (@talex5 ocaml-multicore/eio#232). Bug fixes / minor changes: - Allow IO to happen even if a fiber keeps yielding (@TheLortex @talex5 ocaml-multicore/eio#213). - Fallback for `traceln` without an effect handler (@talex5 ocaml-multicore/eio#226). `traceln` now works outside of an event loop too. - Check for cancellation when creating a non-protected child context (@talex5 ocaml-multicore/eio#222). - eio_linux: handle EINTR when calling `getrandom` (@bikallem ocaml-multicore/eio#212). - Update to cmdliner.1.1.0 (@talex5 ocaml-multicore/eio#190).
When using Eio I encountered an issue while porting a test in the mirage
arplibrary.The failing code can be reduced to the following:
I expected this code to finish after
0.1sof execution, but instead it does an infinite loop. This is because the scheduler prioritizes paused fibers before sleeping fibers that are now due.This PR adds a
Zzz.popbefore checking for paused fibers.