Skip to content

Domains spawned by the Domain Manager do not inherit fiber-local state #807

@dijkstracula

Description

@dijkstracula

Dear Eio maintainers,

I have been debugging a potentially surprising result this morning.

Consider a program with a fiber-local value i.

utop # #require "eio_main";;
utop # open Eio;;
utop # let i: int Fiber.key = Fiber.create_key ();;
val i : int Fiber.key = <abstr>

Quite sensibly, forked fibers inherit their parent's fiber-local state:

[code listing 1]
utop # Eio_main.run (fun env ->
  Switch.run (fun sw ->
    Fiber.with_binding i 42 (fun () ->
      Fiber.fork ~sw (fun () ->
        let o = Option.map (Printf.sprintf "%d") (Fiber.get i) in
        let v = Option.value o ~default:"???" in
        Flow.copy_string v (Stdenv.stdout env)))));;
42- : unit = ()

Now, what happens in a domain-spawning situation? One would reasonably expect the following Eio loop, which manually spawns a domain, to bomb out, because said spawn is happening outside the purview of Eio, and so there's no mechanism to associate the "parent" fiber's context with whatever state is inside the domain, much less handle any effects!

[code listing 2]
utop #  Eio_main.run (fun env ->
  Switch.run (fun sw ->
    Fiber.with_binding i 42 (fun () ->
      Domain.join @@ Domain.spawn @@ fun () -> Fiber.get i)));;
Exception: Stdlib.Effect.Unhandled(Eio__core__Cancel.Get_context)

Of course, we have a solution for Eio and domains: Domain_manager! Noting that the docs for Domain_manager state that "[the function passed to Domain_manager.run] must only access thread-safe values from the calling domain", and fiber-local state is definitionally thread-safe, I would expect a combination of the behaviour of code listing 1 and code listing 2: accessing a FLS value within a domain managed by Domain_manager should inherit the parent's value, but it does not: Instead of Some 42, we get None back:

[code listing 3]
utop #  Eio_main.run (fun env ->
  Switch.run (fun sw ->
    let domain_mgr = Eio.Stdenv.domain_mgr env in
    Fiber.with_binding i 42 (fun () ->
      Domain_manager.run domain_mgr @@ fun () -> Fiber.get i)));;
- : int option = None

This feels like an omission to me - clearly the spawned domain is able to handle effects (we do not fail to get the context like in code listing 2), and the fact that Eio uses lambda capabilities as a core design suggests that "lexical scoping" in this manner ought to produce the value I expected. What do the Eio maintainers think? (If you feel the current behaviour is the correct one, I'd suggest at minimum being explicit about this in the docs.)

Thanks!

Nathan

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions