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

Extended supervisor API #132

Open
natefaubion opened this issue Nov 21, 2017 · 2 comments
Open

Extended supervisor API #132

natefaubion opened this issue Nov 21, 2017 · 2 comments

Comments

@natefaubion
Copy link
Collaborator

natefaubion commented Nov 21, 2017

The supervised combinator lets one fork freely and have the runtime automatically clean up fibers when the inner block exits. I think we could potentially extend this API to have more control over things like unhandled exceptions.

Implementation-wise, the supervision context is essentially a MonadReader environment. When we fork anything, we pull out the supervision context and use that to track fibers. I think we could potentially reify this environment, giving authors control over which supervisors run which fibers, and also what happens when they exit abnormally.

As a baseline, I could imagine an API like:

data Supervisor eff

makeSupervisor :: forall eff. Aff eff (Supervisor eff)

-- We need a special global supervisor.
-- Equivalent to `ask`.
getCurrentSupervisor :: forall eff. Aff eff (Supervisor eff)

-- Equivalent to `local`
runWithSupervisor :: forall eff a. Supervisor eff -> Aff eff a -> Aff eff a

-- Tracks Fiber using provided supervisor
forkWithSupervisor :: forall eff a. Supervisor eff -> Aff eff a -> Aff eff (Fiber eff a)

-- Kills all pending Fibers
killAll :: forall eff. Error -> Supervisor eff -> Aff eff Unit

Which we could use to implement existing functionality. supervised would then be something like:

supervised aff = do
  sup <- makeSupervisor
  finally (killAll sup) $ runWithSupervisor sup aff

And fork would be something like:

fork aff = do
  sup <- getCurrentSupervisor
  forkWithSupervisor sup aff

Some possible ways to extend the API:

  • Supervisor delegation: create a context extended from a parent context.
  • Respond to unhandled exceptions.
  • A way to associate some tag/state/process id with a Fiber.
@jdegoes
Copy link
Contributor

jdegoes commented Nov 22, 2017

Really interesting idea of reifying the environment to allow users to do the same sorts of things that right now, only the library itself can perform.

@natefaubion
Copy link
Collaborator Author

Some additions in a generalized exception world:

-- Every Supervisor has a parent Supervisor, propagating back up to a global,
-- root supervisor which just throws exceptions in a fresh stack.
data Supervisor

-- Takes callback for handling panics. The handler Aff is required to not have
-- exceptions, but another panic would delegate to the parent Supervisor.
makeSupervisor :: forall err. (Error -> Aff Void Unit) -> Aff err Supervisor

-- Equivalent to `ask`.
currentSupervisor :: forall err. Aff err Supervisor

-- Equivalent to `local`
runWithSupervisor :: forall err a. Supervisor -> Aff err a -> Aff err a

-- Tracks Fiber using provided Supervisor.
forkWithSupervisor :: forall err a. Supervisor -> Aff err a -> Aff err (Fiber err a)
suspendWithSupervisor :: forall err a. Supervisor -> Aff err a -> Aff err (Fiber err a)

-- Kills all pending Fibers.
killSupervisor :: forall err. Error -> Supervisor -> Aff err Unit

-- Irrecoverable error.
panic :: forall void1 void2. Error -> Aff void1 void2

supervised aff = do
  sup <- makeSupervisor panic
  finally (killSupervisor sup) (runWithSupervisor sup aff)

forkAff aff = do
  sup <- currentSupervisor
  forkWithSupervisor sup aff

runAff k aff = launchAff do
  sup <- makeSupervisor panic
  runWithSupervisor sup (panic =<< (attempt <<< liftEff <<< k) =<< attempt aff)

That is the main purpose of a Supervisor is to observe and report panics. We already have something like that, in that unobserved exceptions results in it being rethrown in a fresh stack. This behavior just becomes the default global Supervisor. The global supervisor will not be accessible to user code, since all entry points for spawning Affs will create a fresh one like in runAff.

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

No branches or pull requests

2 participants