Skip to content
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

discuss: perform GetActiveSwitch #364

Closed
cdaringe opened this issue Nov 6, 2022 · 2 comments
Closed

discuss: perform GetActiveSwitch #364

cdaringe opened this issue Nov 6, 2022 · 2 comments

Comments

@cdaringe
Copy link

cdaringe commented Nov 6, 2022

problem

Counter to the switch best practices section, periodically I need a fiber for some operation, and I want that fiber to explicitly operate within the context of the current switch.

For example, this week I wrote let foo ~sw = Fiber.fork_daemon ~sw ..., s.t. I would gracefully teardown the daemon when everything else cancelled in the current context. Thus, i ended up threading ~sw through my stack.

it would have been lovely to to:

let foo _ = Fiber.fork_daemon ~sw:(perform GetActiveSwitch)` (* GetParentSwitch is probably better *)

and be on my merry way.

I could imagine that supporting such a feature may be prohibitively expensive, but... i don't know the internals! Maybe it's not? :)

I'm not married to the feature/suggestion. Feel free to hastily close, just wanted to offer up the idea.

@patricoferris
Copy link
Collaborator

patricoferris commented Nov 7, 2022

I had a quick look at this and currently without changes I don't think it is possible to provide. I had a quick go by installing a switch providing handler on each Switch.run call, which I think should work, but then that's installing quite a lot of handlers and I think it quickly becomes a system people might abuse instead of being explicit about which switch each fiber is attached to.

I did hit something similar but not quite the same. I think threading through the switch is the right approach if possible even if it is tedious. That being said, there are times in various codebases (particular when porting from Lwt) that I've wanted what I called a "Toplevel switch" and ended up implementing a:

type _ Effect.t += Top_switch : Eio.Switch.t Effect.t

let top_switch () = Effect.perform Top_switch

This was necessary because of the use of top-level lazy values (perhaps there's a better implementation method for these libraries but I was just trying to quickly port them) where there's no opportunity to give them a switch. See mirage/irmin-watcher@master...patricoferris:irmin-watcher:eio#diff-16bfa0907ea8cd48e13be6024d05500ed67de0bca64421a143fe7e8ed4347a7fR58-R83

EDIT: thinking about it some more, I don't think the switch method I mentioned would work across third-party libraries where you could end up with:

let foo () = Fiber.fork_daemon ~sw:(Switch.get_active_switch ()) ...

let run () =
  Switch.run @@ fun sw ->
  ThirdParty.run @@ fun () ->
  foo ()

And unbeknownst to you ThirdParty.run called Switch.run in it.

@talex5
Copy link
Collaborator

talex5 commented Nov 7, 2022

We don't want this by default because it defeats the purpose of having switches, which is knowing the lifetime of resources. e.g. if i do:

Eio_main.run @@ fun _ ->
foo ();
bar ()

then I know that any fibers or file-handles created by foo will have finished before bar runs, because I didn't pass a switch to foo.

Also, there isn't currently a concept of the "current switch" - sometimes there aren't any switches at all (as in the example above)!

However, if you have to do this (e.g. @patricoferris's example of an existing library that provides no way to pass it) you can store a switch in a fiber-local variable (Eio.Fiber.with_binding). But then for any code within that binding, you won't be able to tell easily when things are released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants