wasm_jit_sup (wasm v0.3.0)

View Source

The processes that compile modules, under supervision.

Compiling runs off the calling process, so it is a process, and a process that is not in the supervision tree is one nothing can see, bound or stop. These are in it.

Why the children take no arguments

A child is started empty and then sent the work:

{ok, Pid} = supervisor:start_child(?MODULE, []),
Pid ! {compile, Inst, Limits, Executed}

Passing the instance as a start argument is the ordinary shape and it would copy it twice, once into this supervisor and once into the child. A real instance is 35 MB, and this happens once per module. A message copies it once, straight to the process that needs it.

Why the children are temporary and brutally killed

temporary, because a compile that crashed must not be tried again by the supervisor: whether to try again is wasm_jit's decision and it makes it on the next hot call. Restarting here would recompile with no instance to hand it to.

brutal_kill, because a compile takes tens of seconds and nothing waits for it. Shutting a node down must not wait either. The slot the child reserved is released by wasm_code_slots's monitor when it dies, and the instance's own record of having asked expires on its own, so being killed at any moment costs nothing but the work.

The two processes per compile that are not in this tree

wasm_core's run_compiler spawns the process that runs compile:forms/2, and that process spawns a reaper. Neither is supervised, which this doc says elsewhere is a place where nothing can see, bound or stop a process, so the exception is worth stating.

They are bounded by the sixteen slot reservations rather than by any count of compilers: a compiler that cannot claim a slot gives up at once, and a unit being built holds one. So at most sixteen units are in flight, and a sharded one carries four such processes rather than two, since each pmap worker has a reaper of its own. The supervisor:count_children/1 check below is not the bound; it is soft and racy by design.

And they are stoppable, which is the point of the reaper and is more than can be said for what they replace. compile:forms/2 spawns its worker with spawn_monitor/1, which does not link, so before this the OTP compiler outlived the process that asked for it: a child killed here, or by application:stop/1, left a compiler running to completion holding its copy of the forms with nothing able to see it.

Why the bound is not a supervisor flag

An OTP supervisor has no max_children; that belongs to pool libraries. The bound is a supervisor:count_children/1 check in wasm_jit before asking for a child, and it is a soft one, which is all that is needed: the hard bound is the sixteen code slots, and a compiler that cannot get one gives up at once. The check is there to stop a seventeenth copying an instance to find that out.

Summary

Functions

Start one compiler, which then waits to be told what to compile.

Functions

init/1

start_compiler()

-spec start_compiler() -> {ok, pid()}.

Start one compiler, which then waits to be told what to compile.

The wait has a deadline. A child whose sender died between starting it and sending it the work would otherwise sit in the tree for the life of the node.

start_link()