Hyperlang.Actor (Hyperlang v0.2.0)

Copy Markdown View Source

The GenServer callback module that runs a program's OTP callbacks.

Start an actor with the program value itself:

GenServer.start(Hyperlang.Actor, {spec, program, arg})
GenServer.start_link(Hyperlang.Actor, {spec, program, arg}, name: MyActor)

spec is the Hyperlang.run/4 spec (%{bindings: ..., policy: ..., syscall: ...}), and every callback runs under it. arg is the guest init/1 argument. The program must export init/1; handle_call/3, handle_cast/2, handle_info/2, handle_continue/2, terminate/2 and code_change/3 are used when exported. As OTP does for a callback module without them, an actor drops a message when the program has no handle_info/2 and runs nothing on exit when it has no terminate/2. A missing required callback stops the actor with the typed run/4 error (category: :export). Guest stdout from callbacks is dropped.

The actor runs exactly its program

The actor's init/1 loads program and pins its code slot in one step, so a release or code-pool pressure before init/1 cannot leave the actor without code or with another program's code. The pin holds until the actor exits. It is charged to the actor's scope (spec.policy.scope, else the actor's own default scope), which may pin at most max_code_pins distinct slots. Hyperlang.load/1 loads a program ahead of time; it is optional.

Limits

Every callback is one Hyperlang.run/4 in the actor process, so the per-callback policy limits (wall_ms, max_reductions/budget, heap_words) apply to each callback. The actor-lifetime limits (rate, ceiling) hold for the actor's whole life, with no host wiring.

Errors

GenServer.start/3 returns {:error, {:hyperlang, error}} when init/1 stops:

  • category: :api when the init argument is not {spec_map, %Hyperlang.Program{}, arg};
  • category: :code_pool with details.reason: :pin_limit at the scope bound, or category: :code_pool when no slot of the program's code_pool window is free;
  • the error of the guest init/1.

Supervision

child_spec/1 takes the same {spec, program, arg} tuple:

children = [{Hyperlang.Actor, {spec, program, arg}}]

The child id is Hyperlang.Actor and the restart is :permanent, as for any GenServer. Supervise more than one actor under one supervisor by giving each its own id:

Supervisor.child_spec({Hyperlang.Actor, {spec, program, arg}}, id: {:counter, 1})

A restarted actor runs init/1 again with the same program, so it pins and, if needed, loads the program again.

Summary

Functions

A child specification that starts the actor with GenServer.start_link(Hyperlang.Actor, {spec, program, arg}).

Functions

child_spec(init_arg)

@spec child_spec({map(), Hyperlang.Program.t(), term()}) :: Supervisor.child_spec()

A child specification that starts the actor with GenServer.start_link(Hyperlang.Actor, {spec, program, arg}).