std.reactor
For outside operations begun now and finished later: what lets a
scheduler’s threads go on while one of them waits on a socket, a name
being resolved or an HTTP request. The C backend’s alone (its
provider, and the runtime’s reactor): a page’s providers do not wait.
Import it by a name, import std.reactor as Reactor. A scheduler waits
on the world through it — cml’s scheduledWithReactor — and so does a
program that starts several operations itself.
import std.reactor as Reactorimport std.duration as Durationimport std.time as Time
-- | three sleeps begun at once, waited for together: in about 100 msslept : {e : HFunctor} -> {{rx : Reactor.reactor <: e}} -> {{tm : Time.time <: e}} -> Co e Intslept = do g <- Reactor.open _ <- Reactor.start g (bind (Time.sleep (Duration.milliseconds 100)) (\u -> pure 1)) _ <- Reactor.start g (bind (Time.sleep (Duration.milliseconds 100)) (\u -> pure 2)) _ <- Reactor.start g (bind (Time.sleep (Duration.milliseconds 100)) (\u -> pure 3)) a <- Reactor.finished g True b <- Reactor.finished g True c <- Reactor.finished g True Reactor.close g pure (List.sum (a ++ b ++ c))These wait in the reactor, where they are started in a group:
- std.net’s
read,write,acceptandwithConnection; - std.filesystem’s reads and writes of a pipe, a terminal or a socket;
- std.process’s
wait, and reads and writes of a command’s pipes; - std.http’s requests;
- std.time’s sleep;
- std.signal’s
wait.
A regular file is read and written at once, the program waiting.
A group is opened, on a domain; each computation started in it — one
of the world, run there until it is done or until an operation of
these would wait, which is then pending, the rest of the computation
kept with it — and the group asked which of those pending are
finished, each its computation’s value, run to its end once its
operation was done. finished g True waits for the first to finish,
in one poll, or until another domain wakes it (wake). A group
within another (openWithin) is pending and finishes among the
other’s, and is closed apart, what is pending in it let go of. Where
no group is being started in, an operation waits where it is, the
whole program with it. Each thread of the system has its own reactor:
a group is its domain’s, started in, asked and closed there alone, but
woken from any.
A group within another is given up from any domain by what giving
makes for it, a thing a custodian can have in its custody
(Custodian.owned): ended — by the custodian’s shutdown, or as the
program lets go of it — the group’s domain gives the group up at its
next look at its finished: what of it is pending let go of, undone (a
socket’s watch, a sleep’s timer, a signal’s or a command’s wait taken
out), and the value giving was given finished in its place; where
none of it is pending, closed alone. What a scheduler gives up by it
is what has no use any more — an act awaited in a custody shut down
— never what a thread or a behaviour is in the middle of.
A computation is started between a Begin and an End of its group,
in the world’s own course, not held by an operation: a handler between
the scheduler and the world — a state — sees what it does as it sees
any other operation, and keeps it.
Group
data Group t = MkGroup IntA group of computations started, each of which comes to a t.
Giving
data GivingWhat gives up a group within another, from any domain: the runtime’s object.
Ending
type Ending x = (r : Type) -> ((t : Type) -> t -> (Maybe t -> x) -> r) -> rA computation started come to its end: its value, given to what comes after its start, whatever it is.
Finished
type Finished x = (r : Type) -> ((t : Type) -> (List t -> x) -> r) -> rWhat a group’s finished are given to, whatever they are.
endingWith
endingWith : {t x : Type} -> t -> (Maybe t -> x) -> Ending xfinishedWith
finishedWith : {t x : Type} -> (List t -> x) -> Finished xReactorF
data ReactorF (m : Type -> Type) x = Open (Maybe ((r : Type) -> ((t : Type) -> (String -> t) -> r) -> r)) (Int -> x) | Begin Int (Bool -> x) | End Int (Ending x) | Finish Int Bool (Finished x) | Close Int x | Within Int (Int -> x) | Wake Int x | Pending Int (Int -> x) | MakeGiving Int ((r : Type) -> ((t : Type) -> t -> r) -> r) (Giving -> x) | CloseGiving Giving xreactor
reactor : HFunctorFor host operations begun now and finished later (a transfer, a socket readable): a group opened, operations begun in it and ended, a wait for one to finish, the group closed.
open
open : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> {failed : Maybe (String -> t) := Nothing} -> Co e (Group t)A group of its own. failed, where it is given, is what a
computation started in it that fails — abandons, or fails in the
runtime — comes to, given why, among the group’s finished, the
others going on; where it is not, the failure is the program’s.
start
start : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> Co e t -> Co e (Maybe t)c started in the group: run until it is done, Just its value;
or until an operation of it would wait, Nothing, its value to be
one of the group’s finished. Begun (True), c is run, and
ended with its value; where it waits, its start is told so
(False), and what comes after its wait is run once that is done,
up to its end, its value then one of the group’s finished.
finished
finished : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> Bool -> Co e (List t)The group’s computations finished since it was last asked, in the
order they were: where wait, waiting for one at least, while any
of it is pending, or until the group is woken (wake); none where
none is.
close
close : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> Co e UnitThe group closed: what of it is pending let go of, undone — a socket being connected closed, a request let go of — and what of it is finished and not asked for.
openWithin
openWithin : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> Co e (Group t)A group within g, of the same domain: what is started in it is
among g’s pending, and finishes among g’s finished; closed, what
of it is pending is let go of, undone, and g stays open. So one
of a group’s computations started is let go of alone, in a group of
its own within it.
pending
pending : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> Co e IntHow many of the group’s computations started are pending now: of the groups within it too.
wake
wake : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> Co e UnitThe domain asking for the group’s finished woken, from any domain:
its finished, waiting, comes back — with none, where none has
finished — and where it is not waiting now, its next does, at
once. A group’s number is no other domain’s, so the right one is
woken; one closed is woken no more, nor one within another: its
root is.
giving
giving : {e : HFunctor} -> {{rx : reactor <: e}} -> {t : Type} -> Group t -> t -> Co e GivingWhat gives up g, a group within another, from any domain: ended
— in a custodian’s custody, by its shutdown; or as the program
lets go of it — the domain of g gives it up at its next look at
its root’s finished: where any of it is pending then, it is closed,
what is pending let go of, undone, and gave is among the root’s
finished in its place; where none is, it is closed alone. So what
counts what was started knows each is done, given up or not. A
group closed is given up no more: closeGiving closes it, where it
is done with, before the program lets go of what gives it up.
closeGiving
closeGiving : {e : HFunctor} -> {{rx : reactor <: e}} -> Giving -> Co e UnitThe group gv gives up closed, on its domain, as close closes it:
given up no more.