-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fiber-local storage #256
Fiber-local storage #256
Conversation
a787f98
to
afab28c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - this looks good!
I'm not sure about the interface either. I've made a branch experimenting with some naming changes here: https://github.com/ocaml-multicore/eio/compare/main...talex5:eio:fiber-storage?expand=1
- I moved the basic get and set functions to
Fiber
(e.g.Fiber.get key
), which avoids the word "context" and maybe makes it more obvious they're attached to fibers. It could do with an introduction explaining what fiber-local variables are for, and maybe discouraging over-use of them. - Renamed
context
tovars
inFiber_context
. - Renamed
with_value
->with_binding
. We might want awithout_binding
too. - I removed the convenience
Fiber_context.make
. I think it's a bit clearer to be explicit here. For example,Fiber.any
really only needs to get them once. - I removed the code to inherit across domains for now. I think this is a bit risky, because the values may not be thread-safe.
Domain
has asplit_from_parent
argument (https://github.com/ocaml/ocaml/blob/8dda88493c0e8bfec83d3d40129bc6e0fb7cef29/stdlib/domain.mli#L95). Possibly something like that will be useful, but we can add it later. - For now, I removed the API to get and set the whole set at once (only domains needed it). If it comes back, it should probably go in
Private
.
Do you think this is better?
Oh, that's a fantastic change. That makes things much nicer!
Ahh, that's a good point. And agreed - I added this going "weelllll, I can see when this might be useful", but probably worth deferring until there's more concrete use-cases. Are you happy for me pull in your changes and then polish docs off, etc...? |
That would be great - thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fleshed out the docs a little. I'm afraid I found it much harder to write a succinct introduction to them, and I think what I've ended up with is a possibly a little too sparse.
lib_eio/core/fiber.ml
Outdated
@@ -128,7 +132,8 @@ let any fs = | |||
| [] -> await_cancel () | |||
| [f] -> wrap f; [] | |||
| f :: fs -> | |||
let new_fiber = Cancel.Fiber_context.make ~cc in | |||
let vars = Cancel.Fiber_context.get_vars () in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we want to hoist this outside the loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please (I was just lazy).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure if it was worth checking if the list was empty here - I'd personally argue not, as any []
should be pretty rare, but happy to handle it if you disagree!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine I think. If we really cared about optimising the empty-list case, we'd do it at the start of the function anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is looking good :-)
lib_eio/core/fiber.ml
Outdated
@@ -128,7 +132,8 @@ let any fs = | |||
| [] -> await_cancel () | |||
| [f] -> wrap f; [] | |||
| f :: fs -> | |||
let new_fiber = Cancel.Fiber_context.make ~cc in | |||
let vars = Cancel.Fiber_context.get_vars () in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please (I was just lazy).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is still marked as a draft, but it looks ready to merge to me!
lib_eio/core/fiber.ml
Outdated
@@ -128,7 +132,8 @@ let any fs = | |||
| [] -> await_cancel () | |||
| [f] -> wrap f; [] | |||
| f :: fs -> | |||
let new_fiber = Cancel.Fiber_context.make ~cc in | |||
let vars = Cancel.Fiber_context.get_vars () in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine I think. If we really cared about optimising the empty-list case, we'd do it at the start of the function anyway.
Thanks! |
Thank you for all your help too! Looking forward to using this :) |
CHANGES: Note: Eio 0.4 drops compatibility with OCaml 4.12+domains. Use OCaml 5.0.0~alpha1 instead. API changes: - `Eio.Dir` has gone. Use `Eio.Path` instead (@talex5 ocaml-multicore/eio#266 ocaml-multicore/eio#270). - `Eio_unix.FD.{take,peek}` were renamed to `take_opt`/`peek_opt` to make way for non-optional versions. New features: - Fiber-local storage (@SquidDev ocaml-multicore/eio#256). Attach key/value bindings to fibers. These are inherited across forks. - `Eio.Path.{unlink,rmdir,rename}` (@talex5 ocaml-multicore/eio#264 ocaml-multicore/eio#265). - `Eio_main.run` can now return a value (@talex5 ocaml-multicore/eio#263). This is useful for e.g. cmdliner. - `Eio_unix.socketpair` (@talex5 ocaml-multicore/eio#260). - `Fiber.fork_daemon` (@talex5 ocaml-multicore/eio#252). Create a helper fiber that does not prevent the switch from exiting. - Add `Fiber.{iter,map,filter,fiter_map}` (@talex5 ocaml-multicore/eio#248 ocaml-multicore/eio#250). These are concurrent versions of the corresponding operations in `List`. Bug fixes: - Fix scheduling fairness in luv backend (@talex5 ocaml-multicore/eio#269). - Implement remaining shutdown commands for luv (@talex5 ocaml-multicore/eio#268). - Fix IPv6 support with uring backend (@haesbaert ocaml-multicore/eio#261 ocaml-multicore/eio#262). - Use `Eio.Net.Connection_reset` exception in more places (@talex5 ocaml-multicore/eio#257). - Report use of closed FDs better (@talex5 ocaml-multicore/eio#255). Using a closed FD could previously cause the whole event loop to exit. - Some fixes for cancellation (@talex5 ocaml-multicore/eio#254). - Ensure `Buf_write` still flushes if an exception is raised (@talex5 ocaml-multicore/eio#246). - Do not allow close on `accept_fork` socket (@talex5 ocaml-multicore/eio#245). Documentation: - Document integrations with Unix, Lwt and Async (@talex5 ocaml-multicore/eio#247). - Add a Dockerfile for easy testing (@talex5 ocaml-multicore/eio#224).
CHANGES: Note: Eio 0.4 drops compatibility with OCaml 4.12+domains. Use OCaml 5.0.0~alpha1 instead. API changes: - `Eio.Dir` has gone. Use `Eio.Path` instead (@talex5 ocaml-multicore/eio#266 ocaml-multicore/eio#270). - `Eio_unix.FD.{take,peek}` were renamed to `take_opt`/`peek_opt` to make way for non-optional versions. New features: - Fiber-local storage (@SquidDev ocaml-multicore/eio#256). Attach key/value bindings to fibers. These are inherited across forks. - `Eio.Path.{unlink,rmdir,rename}` (@talex5 ocaml-multicore/eio#264 ocaml-multicore/eio#265). - `Eio_main.run` can now return a value (@talex5 ocaml-multicore/eio#263). This is useful for e.g. cmdliner. - `Eio_unix.socketpair` (@talex5 ocaml-multicore/eio#260). - `Fiber.fork_daemon` (@talex5 ocaml-multicore/eio#252). Create a helper fiber that does not prevent the switch from exiting. - Add `Fiber.{iter,map,filter,fiter_map}` (@talex5 ocaml-multicore/eio#248 ocaml-multicore/eio#250). These are concurrent versions of the corresponding operations in `List`. Bug fixes: - Fix scheduling fairness in luv backend (@talex5 ocaml-multicore/eio#269). - Implement remaining shutdown commands for luv (@talex5 ocaml-multicore/eio#268). - Fix IPv6 support with uring backend (@haesbaert ocaml-multicore/eio#261 ocaml-multicore/eio#262). - Use `Eio.Net.Connection_reset` exception in more places (@talex5 ocaml-multicore/eio#257). - Report use of closed FDs better (@talex5 ocaml-multicore/eio#255). Using a closed FD could previously cause the whole event loop to exit. - Some fixes for cancellation (@talex5 ocaml-multicore/eio#254). - Ensure `Buf_write` still flushes if an exception is raised (@talex5 ocaml-multicore/eio#246). - Do not allow close on `accept_fork` socket (@talex5 ocaml-multicore/eio#245). Documentation: - Document integrations with Unix, Lwt and Async (@talex5 ocaml-multicore/eio#247). - Add a Dockerfile for easy testing (@talex5 ocaml-multicore/eio#224).
I'm not super happy with the interface for this, so felt it was worth opening an issue and starting a bit of a discussion before spending any more time polishing this off.
Some comments:
I initially went for
Context
to describe the store of fibre-local variables, sort of mimicking Async'sExecution_context
and Python'scontextvars
s library. However, I'm not sure this is the best term - it definitely ends up confusing as eio also hasFiber_context.t
.The store of fibre-local variables is inherited from the currently running fibre when forking (rather than the passed switch). This is (at least in my minds) more intuitive, but does involve an extra
Effect.perform Get_context
call on each fork, which obviously comes with some performance cost.