Allow sharing of libuv poll handles#279
Conversation
|
I had a look at lwt_luv to see how they handle it: open Lwt.Infix
let () =
let () = Lwt_engine.set (new Lwt_luv.engine) in
let socket, _ = Lwt_unix.(socketpair PF_UNIX SOCK_STREAM 0) in
Lwt_main.run begin
let _a = Lwt_unix.wait_read socket in
let rec aux () =
Lwt_unix.wait_write socket >>= fun () ->
Lwt_unix.write socket (Bytes.of_string "a") 0 1 >>= fun (_ : int) ->
aux ()
in
aux ()
endThe result is: However, Lwt does handle the case of multiple readers OK (just not a mix of readers and writers):
You probably want a
I don't think this will work. http://docs.libuv.org/en/v1.x/poll.html#c.uv_poll_start says:
However, it also seems unnecessary:
It's not clear to me what happens to the callback if you call |
|
Yes, I'm not really sure what the actual behaviour is. From the source code, it would seem after calling In b12c0d6 I've made it such that callbacks are the same and a cancelled fiber will remove itself from the list of continuations to enqueue so the In 4999630 I've added the extra logic to handle simultaneously awaiting for writability and readability on a FD. The bit mask for events is updated to whatever you pass to |
There was a problem hiding this comment.
This looks good to me (apart from minor whitespace issues). I've put some suggested simplifications at 4985168. Feel free to include those if they look sensible. I put fd in the fd_event_waiters. We have to store it anyway, and it simplifies things to have it here rather than captured in a closure. Sorry about the extra re-indentation!
Do we need to fail all waiters if close is called too?
|
Yes your changes are very sensible :)) What tool do you use for indentation and whitespaces, ocp-indent?
This seems reasonable, the docs say:
It seems reasonable to me to fail any suspended fibers, would this approach work on Linux? |
bf31024 to
1304f64
Compare
talex5
left a comment
There was a problem hiding this comment.
What tool do you use for indentation and whitespaces, ocp-indent?
I use ocp-indent for indentation. For whitespace, .git/hooks/pre-commit.sample will detect trailing spaces (if you remember to enable it).
It seems reasonable to me to fail any suspended fibers, would this approach work on Linux?
I'm not sure if it's worth doing. I think Linux only uses the FD to look up the file object when you submit the request, and it's quite happy to continue waiting for the file after the FD is closed. It's more a limitation of luv that files can't be used after their FD is closed. Maybe it's needed on Windows? Though it might be nice to be consistent.
I've pushed a couple more changes:
- One removes the use of
Obj.magicby stashing the FD away at the start. Using Obj.magic with an external library like this seems pretty risky. - I got it to update the poll mask in the case where one queue becomes empty but the other isn't. It's hard to test for this because it just uses a lot of CPU if you forget, but doesn't stop working. I also moved the code around a bit to group all the polling stuff together.
If that looks OK to you, I'm happy to merge it.
Hmm, this doesn't work for all handles. I guess the FD isn't known initially in some cases. |
0dbda02 to
d6792e2
Compare
Calling [File.close] from a cancelled context would mark it as closed but abort doing the actual close.
d6792e2 to
8408dd4
Compare
|
OK, fixed it by adding a constraint. It seems we only ever use handles types that may have FDs anyway. I also got it to ignore EBADF when getting the FD, since sockets don't get their FD set immediately (e.g. a failure after |
Ah, I wasn't sure if that was the case. Definitely better than |
|
Thanks! |
CHANGES: New features: - Add `Eio.Condition` (@TheLortex @talex5 ocaml-multicore/eio#277). Allows a fiber to wait for some condition to become true. - Add `Eio.Net.getaddrinfo` and `getnameinfo` (@bikallem @talex5 ocaml-multicore/eio#278 ocaml-multicore/eio#288 ocaml-multicore/eio#291). Convert between host names and addresses. - Add `Eio.Debug` (@talex5 ocaml-multicore/eio#276). Currently, this allows overriding the `traceln` function. - `Buf_write.create`: make switch optional (@talex5 ocaml-multicore/eio#283). This makes things easier for people porting code from Faraday. Bug fixes: - Allow sharing of libuv poll handles (@patricoferris @talex5 ocaml-multicore/eio#279). Luv doesn't allow two callers to watch the same file handle, so we need to handle that in Eio. Other changes: - Upgrade to uring 0.4 (@talex5 ocaml-multicore/eio#290). - Mention `Mutex`, `Semaphore` and `Condition` in the README (@talex5 ocaml-multicore/eio#281).
Multiple poll handles for the same file descriptor are not allowed by libuv as @talex5 noted in ocaml-multicore/lwt_eio#11
This can happen seemingly quite easily, for example:
This PR makes it so we keep track of the various fibers that are waiting on read and write events and we enqueue them accordingly based on the events that the polling callback receives. This means the same handle must register for both kinds of events and then we decided how to handle them rather than having a dedicated
READABLEand a dedicatedWRITABLEpoll handle.So far this PR fixes both the simple example and the origin Irmin example that caused me to find this (code here), however I haven't tried:
Lf_queueand I'm using theLf_queuesize to check whether there are any fibers waiting on the handle -- perhaps an integer that we decrement/increment would suffice here?