supervisor(3)
π NAME π
supervisor β the generic supervisor behavior. And yes, bestie, this is supervisor(3) straight out of the Erlang Module Definition section, so buckle up because this one is a whole personality. π
β¨ DESCRIPTION β¨
Okay so, real talk: this behavior module hands you a supervisor, and a supervisor is just a process whose entire job is supervising other processes, which we call child processes. And a child process? It can be another supervisor (yes, supervisors supervising supervisors, it's giving dynasty π«Ά) or a worker process. Worker processes are normally built with one of the gen_event, gen_server, or gen_statem behaviors. So many icons in one ecosystem, we love to see it.
A supervisor implemented with this module gets a standard set of interface functions, plus functionality for tracing and error reporting. Supervisors are what you use to build a hierarchical process structure called a supervision tree, which is honestly just a really nice way to structure a fault-tolerant application. If you want the deep lore, go read Supervisor Behaviour in OTP Design Principles. π
A supervisor expects you to define which child processes it should supervise, and you do that by specifying them in a callback module that exports a predefined set of functions. No callback module, no children, no vibes.
Heads up bestie: unless it says otherwise, every function in this module fails if the supervisor you named doesn't exist, or if you passed bad arguments. That's not a bug, that's just how it is. π
π¦ Supervision Principles π¦
The supervisor is responsible for starting, stopping, and monitoring its child processes. That's the whole job description. The basic idea β the core thesis, the main character energy β is that a supervisor must keep its child processes alive by restarting them whenever necessary. Periodt.
The children of a supervisor are defined as a list of child specifications. When the supervisor starts, the child processes get started in order, left to right, according to that list. When the supervisor is about to terminate, it first terminates its child processes in reversed start order, so, right to left. Symmetry, we stan. π
π Supervisor flags π
The supervisor's properties are defined by the supervisor flags. The type definition goes like this, and I'm showing it verbatim because you do not want to get this wrong:
sup_flags() = #{strategy => strategy(), % optional
intensity => non_neg_integer(), % optional
period => pos_integer(), % optional
hibernate_after => timeout(), % optional, available since OTP 28.0
auto_shutdown => auto_shutdown()} % optional
π₯ Restart Strategies π₯
A supervisor can have one of the following restart strategies, and you pick it with the strategy key in that map up there. Choose wisely, queen. π
one_for_oneβ if one child process terminates and is to be restarted, only that child process is affected. Nobody else gets dragged into it. This is the default restart strategy. β¨one_for_allβ if one child process terminates and is to be restarted, then all the other child processes are terminated, and then all child processes are restarted. One falls, everybody falls, then everybody gets back up together. Dramatic but effective. πrest_for_oneβ if one child process terminates and is to be restarted, the 'rest' of the child processes get terminated β that means the child processes that come after the terminated child process in the start order. Then the terminated child process and all child processes after it are restarted. It's a "you and everyone behind you in line" situation.simple_one_for_oneβ a simplifiedone_for_onesupervisor, where all child processes are dynamically added instances of the same process type, like, running the same code. Same girl, many copies. π«Ά- Functions
delete_child/2andrestart_child/2are invalid forsimple_one_for_onesupervisors and return{error,simple_one_for_one}if the specified supervisor uses this restart strategy. - Function
terminate_child/2can be used for children undersimple_one_for_onesupervisors, by specifying the child'spid/0as the second argument. If instead you use the child specification identifier,terminate_child/2returns{error,simple_one_for_one}. - Because a
simple_one_for_onesupervisor can have many children, it shuts them all down asynchronously. That means the children do their cleanup in parallel, and therefore the order in which they are stopped is not defined. No line, no order, chaos but organized chaos.
- Functions
β³ Restart intensity and period β³
To prevent a supervisor from getting stuck in an infinite loop of child process terminations and restarts (the worst possible cycle, ngl), a maximum restart intensity is defined using two integer values, specified with the intensity and period keys in that same map.
So, if we call the intensity value MaxR and the period value MaxT: if more than MaxR restarts occur within MaxT seconds, the supervisor terminates all child processes and then terminates itself. And the termination reason for the supervisor itself in that case will be shutdown. So if you see shutdown, that might be why, bestie. π
intensity defaults to 1 and period defaults to 5.
π΄ Hibernate after π΄
To save memory, a supervisor β like literally any other process β can go into hibernation. It's a little nap, a little beauty sleep, we respect it. π
By default, a simple_one_for_one supervisor will never hibernate, because it's expected that its children will come and go at potentially high rates. Meanwhile, the other strategies expect their children to be more stable, so they default to hibernating after a certain period of time of inactivity β which keeps them responsive to sudden bursts of restarts while saving memory during the calm, stable periods.
You can finetune this flag by setting hibernate_after. For example, if the supervisor is going to be regularly queried with which_child/1 or something similar, and you want hibernation to be better controlled. Your tree, your rules. π
π Automatic Shutdown π
A supervisor can be configured to automatically shut itself down with exit reason shutdown when significant children terminate. You set this with the auto_shutdown key in the flags map:
neverβ automatic shutdown is disabled. This is the default setting.
With auto_shutdown set to never, child specs with the significant flag set to true are considered invalid and will be rejected. Not "ignored", not "warned about" β rejected. π
any_significantβ the supervisor will shut itself down when any significant child terminates. That means: when atransientsignificant child terminates normally, or when atemporarysignificant child terminates normally or abnormally.all_significantβ the supervisor will shut itself down when all significant children have terminated, that is, when the last active significant child terminates. The same rules as forany_significantapply.
For more info, go read the section Automatic Shutdown in Supervisor Behavior in OTP Design Principles. π
WARNING: The automatic shutdown feature appeared in OTP 24.0, but applications using this feature will also compile and run with older OTP versions. However β and this is the scary part β such applications, when compiled with an OTP version that predates the appearance of the automatic shutdown feature, will leak processes, because the automatic shutdowns they rely on will not happen. It is up to implementors to take proper precautions if they expect that their applications may be compiled with older OTP versions. Don't let your processes just wander off into the void. π«
πΈ Child specification πΈ
The type definition of a child specification is as follows, verbatim, no edits, character for character:
child_spec() = #{id => child_id(), % mandatory
start => mfargs(), % mandatory
restart => restart(), % optional
significant => significant(), % optional
shutdown => shutdown(), % optional
type => worker(), % optional
modules => modules()} % optional
The old tuple format is kept for backwards compatibility β see child_spec/0 β but the map is preferred. Old format, we appreciate you, but the map ate. π
idβ used to identify the child specification internally by the supervisor.
The id key is mandatory.
Notice that this identifier has, on occasion, been called "name". As far as possible, the terms "identifier" or "id" are now used, but to keep backward compatibility, some occurrences of "name" can still be found β for example in error messages. Old habits, iykyk.
startβ defines the function call used to start the child process. It must be a module-function-arguments tuple{M,F,A}used asapply(M,F,A). Classic.
The start function must create and link to the child process, and must return {ok,Child} or {ok,Child,Info}, where Child is the pid of the child process and Info is any term that is ignored by the supervisor. Ignored! The supervisor does not care about your Info, bestie, it just takes the pid and moves on. π
The start function can also return ignore if the child process, for some reason, cannot be started. In that case the child specification is kept by the supervisor (unless it is a temporary child) but the non-existing child process is ignored. No process, no problem.
If something goes wrong, the function can also return an error tuple {error,Error}.
Notice that the start_link functions of the different behavior modules fulfill all of the above requirements. Handy, right? β¨
The start key is mandatory.
restartβ defines when a terminated child process must be restarted.- A
permanentchild process is always restarted. - A
temporarychild process is never restarted β even when the supervisor's restart strategy isrest_for_oneorone_for_alland a sibling's death causes the temporary process to be terminated. RIP, no respawn. π - A
transientchild process is restarted only if it terminates abnormally, that is, with an exit reason other thannormal,shutdown, or{shutdown,Term}.
- A
The restart key is optional. If it is not specified, it defaults to permanent.
significantβ defines if a child is considered significant for automatic self-shutdown of the supervisor.
Setting this option to true when the restart type is permanent is invalid. Also, it is considered invalid to start children with this option set to true in a supervisor when the auto_shutdown supervisor flag is set to never. Them's the rules. π
The significant key is optional. If it is not specified, it defaults to false.
shutdownβ defines how a child process must be terminated.brutal_killmeans the child process is unconditionally terminated usingexit_signal(Child,kill). Immediate. No negotiation. πͺ- An integer time-out value means the supervisor tells the child process to terminate by calling
exit_signal(Child,shutdown)and then waits for an exit signal with reasonshutdownback from the child process. If no exit signal is received within the specified number of milliseconds, the child process is unconditionally terminated usingexit_signal(Child,kill). So it's a "please" followed by "okay fine".
If the child process is another supervisor, the shutdown time must be set to infinity, to give the subtree ample time to shut down. Give the whole family time to pack, basically. π§³
WARNING: Setting theshutdowntime to anything other thaninfinityfor a child of typesupervisorcan cause a race condition, where the child in question unlinks its own children but fails to terminate them before it is killed. Genuinely cursed behaviour, do not do this. π
It is also allowed to set it to infinity if the child process is a worker.
WARNING: Be careful when setting theshutdowntime toinfinitywhen the child process is a worker. Because, in this situation, the termination of the supervision tree depends on the child process, it must be implemented in a safe way, and its cleanup procedure must always return. Forever is a long time to wait on someone who might never come back, bestie. π«Ά
(part 1 done β more coming, don't go anywhere πΈ)
Notice that all child processes implemented using the standard OTP behaviour modules automatically adhere to the shutdown protocol. iconically well-behaved of them, honestly β¨
The shutdown key is optional, bestie. If you don't specify it, it defaults to 5000 when the child is of type worker, and it defaults to infinity when the child is of type supervisor.
typeπ§ β tells you whether the child process is asupervisoror aworker.
The type key is optional too. Don't specify it? It defaults to worker.
modulesπ β the release handler reads this during code replacement to work out which processes are using a certain module. Rule of thumb, fr fr: if the child process is asupervisor,gen_server, orgen_statem, this is to be a list with one element[Module], whereModuleis the callback module. If the child process is an event manager (gen_event) with a dynamic set of callback modules, valuedynamicmust be used. Want the whole lore on release handling? See Release Handling in OTP Design Principles.
The modules key is optional. If it's not specified, it defaults to [M], where M comes from the child's start {M,F,A}.
- Internally, the supervisor also keeps track of the pid
Childof the child process, orundefinedif no pid exists. Little bookkeeping queen π
π See Also π
gen_event, gen_statem, gen_server, sys
π DATA TYPES π
auto_shutdown()
auto_shutdown() :: never | any_significant | all_significant.
child()
child() :: undefined | pid().
child_id()
child_id() :: term().
Not a pid/0. nope, not that kind of vibe π
child_rec()
child_rec() :: #child{}.
child_spec()
child_spec() ::
#{id := child_id(),
start := mfargs(),
restart => restart(),
significant => significant(),
shutdown => shutdown(),
type => worker(),
modules => modules()} |
{Id :: child_id(),
StartFunc :: mfargs(),
Restart :: restart(),
Shutdown :: shutdown(),
Type :: worker(),
Modules :: modules()}.
The tuple format is kept for backward compatibility only. A map is preferred β see more details above. (yes, this is the "please stop using the old one" of it all π)
children()
children() ::
{Ids :: [child_id()], Db :: #{child_id() => child_rec()}}.
mfargs()
mfargs() ::
{M :: module(), F :: atom(), A :: [term()] | undefined}.
Value undefined for A (the argument list) is only to be used internally in supervisor. If the restart type of the child is temporary, the process is never to be restarted and therefore there is no need to store the real argument list. Value undefined is then stored instead. lowkey efficient π
modules()
modules() :: [module()] | dynamic.
restart()
restart() :: permanent | transient | temporary.
shutdown()
shutdown() :: brutal_kill | timeout().
significant()
significant() :: boolean().
startchild_err()
startchild_err() ::
already_present | {already_started, Child :: child()} | term().
startchild_ret()
startchild_ret() ::
{ok, Child :: child()} |
{ok, Child :: child(), Info :: term()} |
{error, startchild_err()}.
startlink_err()
startlink_err() ::
{already_started, pid()} | {shutdown, term()} | term().
startlink_ret()
startlink_ret() :: {ok, pid()} | ignore | {error, startlink_err()}.
strategy()
strategy() ::
one_for_all | one_for_one | rest_for_one | simple_one_for_one.
sup_flags()
sup_flags() ::
#{strategy => strategy(),
intensity => non_neg_integer(),
period => pos_integer(),
auto_shutdown => auto_shutdown(),
hibernate_after => timeout()} |
{RestartStrategy :: strategy(),
Intensity :: non_neg_integer(),
Period :: pos_integer()}.
The tuple format is kept for backward compatibility only. A map is preferred β see more details above. π₯
sup_name()
sup_name() ::
{local, Name :: atom()} |
{global, Name :: term()} |
{via, Module :: module(), Name :: any()}.
This is the name specification you use when starting a supervisor. Peep start_link/2,3 and the sup_ref/0 type below π
{local,LocalName}β registers the supervisor locally asLocalName, usingregister/2.{global,GlobalName}β registers the supervisor process id globally asGlobalName, usingglobal:register_name/2.{via,RegMod,ViaName}β registers the supervisor process with the registry represented byRegMod. TheRegModcallback is to export the functionsregister_name/2,unregister_name/1,whereis_name/1, andsend/2, which are to behave exactly like the corresponding functions inglobal. So{via,global,GlobalName}is a valid reference equivalent to{global,GlobalName}. π
sup_ref()
sup_ref() ::
(Name :: atom()) |
{Name :: atom(), Node :: node()} |
{global, Name :: term()} |
{via, Module :: module(), Name :: any()} |
pid().
The supervisor specification you use when addressing a supervisor. See count_children/1, delete_child/2, get_childspec/2, restart_child/2, start_child/2, terminate_child/2, which_children/1, plus the sup_name/0 type above.
It can be any of these, bestie:
pid/0β the supervisor's process identifier.LocalNameβ the supervisor is locally registered asLocalNamewithregister/2.{Name,Node}β the supervisor is locally registered on another node.{global,GlobalName}β the supervisor is globally registered inglobal.{via,RegMod,ViaName}β the supervisor is registered in an alternative process registry. The registry callback moduleRegModis to export the functionsregister_name/2,unregister_name/1,whereis_name/1, andsend/2, which are to behave like the corresponding functions inglobal. So{via,global,GlobalName}is the same as{global,GlobalName}. π¦
worker()
worker() :: worker | supervisor.
π₯ FUNCTIONS π₯
π
check_childspecs(ChildSpecs, AutoShutdown) -> Result
check_childspecs(ChildSpecs, AutoShutdown) -> Result
when
ChildSpecs :: [child_spec()],
AutoShutdown ::
undefined | auto_shutdown(),
Result :: ok | {error, Error :: term()}.
Takes a list of child specifications as its argument and hands back ok if every single one of them is syntactically correct. Otherwise you get {error,Error}. no in-between, she's very clear about that π
If the AutoShutdown argument is not undefined, it also checks whether the child specifications are allowed for the given auto_shutdown option.
π count_children(SupRef) -> PropListOfCounts
count_children(SupRef) -> PropListOfCounts
when
SupRef :: sup_ref(),
PropListOfCounts :: [Count],
Count ::
{specs,
ChildSpecCount :: non_neg_integer()} |
{active,
ActiveProcessCount ::
non_neg_integer()} |
{supervisors,
ChildSupervisorCount ::
non_neg_integer()} |
{workers,
ChildWorkerCount :: non_neg_integer()}.
Returns a property list holding the counts for each of these elements of the supervisor's child specifications and managed processes:
specsβ the total count of children, dead or alive. everyone's counted π«Άactiveβ the count of all actively running child processes managed by this supervisor. For asimple_one_for_onesupervisor, no check is done to ensure that each child process is still alive, although the result provided here is likely to be very accurate unless the supervisor is heavily overloaded.supervisorsβ the count of all children marked aschild_type = supervisorin the specification list, regardless of whether the child process is still alive.workersβ the count of all children marked aschild_type = workerin the specification list, regardless of whether the child process is still alive.
β¨ delete_child(SupRef, Id) -> Result
delete_child(SupRef, Id) -> Result
when
SupRef :: sup_ref(),
Id :: child_id(),
Result :: ok | {error, Error},
Error ::
running | restarting | not_found |
simple_one_for_one.
Tells supervisor SupRef to delete the child specification identified by Id. The corresponding child process must not be running, okay? Use terminate_child/2 to terminate it first.
If it works, you get ok. If the child specification identified by Id exists but the corresponding child process is running or is about to be restarted, the function returns {error,running} or {error,restarting}, respectively. And if the child specification identified by Id does not exist at all? {error,not_found}. π
π§ get_childspec(SupRef, Id) -> Result
get_childspec(SupRef, Id) -> Result
when
SupRef :: sup_ref(),
Id :: pid() | child_id(),
Result :: {ok, child_spec()} | {error,
Error},
Error :: not_found.
Returns the child specification map for the child identified by Id under supervisor SupRef. The returned map contains all keys, both mandatory and optional β she's not gatekeeping anything π
πΈ restart_child(SupRef, Id) -> Result
restart_child(SupRef, Id) -> Result
when
SupRef :: sup_ref(),
Id :: child_id(),
Result ::
{ok, Child :: child()} |
{ok, Child :: child(), Info :: term()} |
{error, Error},
Error ::
running | restarting | not_found |
simple_one_for_one |
term().
Tells supervisor SupRef to restart a child process corresponding to the child specification identified by Id. The child specification must exist, and the corresponding child process must not be running.
Heads up though: for temporary children, the child specification is automatically deleted when the child terminates. So it's genuinely not possible to restart such children. gone means gone, bestie π
If the child specification identified by Id doesn't exist, bestie gets {error,not_found}. And if the child spec does exist but the process it points at is already up and running? Then it's {error,running}. Like, it's already there, what more do you want from her π
When the child process start function hands back {ok,Child} or {ok,Child,Info}, that pid gets added to the supervisor and the function throws back the exact same value. Slay.
If the child process start function returns ignore, the pid stays undefined and you get {ok,undefined}. No drama, no child.
But if the start function returns an error tuple, or some erroneous value, or just flat-out fails β you get {error,Error}, where Error is a term holding all the info about what went wrong. π
start_child(SupRef, ChildSpec) -> startchild_ret()
when SupRef :: sup_ref(), ChildSpec :: child_spec();
(SupRef, ExtraArgs) -> startchild_ret()
when SupRef :: sup_ref(), ExtraArgs :: [term()].
Okay so this one dynamically adds a child specification to supervisor SupRef, and that's what kicks off the corresponding child process. It's giving spontaneous adoption β¨
For one_for_one, one_for_all and rest_for_one supervisors, that second argument has to be a valid child specification, ChildSpec. The child process gets started using the start function defined inside that child spec.
But for simple_one_for_one supervisors? Different vibe entirely. The child specification that lives in Module:init/1 is the one that gets used, and the second argument has to be an arbitrary list of terms instead β ExtraArgs. The child process then starts by appending ExtraArgs to the existing start function arguments, i.e. it calls apply(M, F, A++ExtraArgs), where {M,F,A} is the start function from the child specification.
- If a child specification with that identifier is already there,
ChildSpecgets discarded, and the function returns{error,already_present}or{error,{already_started,Child}}β which one depends on whether the child process is running or not. - If the child process start function returns
{ok,Child}or{ok,Child,Info}, the child specification and pid both get added to the supervisor, and the function returns the same value. - If the child process start function returns
ignore, thenChildSpecgets added to the supervisor (if it's anone_for_one,one_for_allorrest_for_onesupervisor, that is) and the pid is set toundefined. Forsimple_one_for_onesupervisors, no child gets added at all. Either way, the function returns{ok,undefined}.
And if the child process start function returns an error tuple, or an erroneous value, or it fails β the child specification gets discarded, and the function returns {error,Error}, where Error is a term containing info about the error and the child specification. π«Ά
start_link(Module, Args) -> startlink_ret()
when Module :: module(), Args :: term().
Creates a nameless supervisor process as part of a supervision tree. Basically the same as start_link/3 except this one doesn't get registered. She's mysterious, she's unregistered, she's thriving π¦
start_link(SupName, Module, Args) -> startlink_ret()
when
SupName :: sup_name(),
Module :: module(),
Args :: term().
Creates a supervisor process as part of a supervision tree. The main character energy π
So for example, this function makes sure the supervisor is linked to the calling process (aka its own supervisor). Chain of command, periodt.
The created supervisor process calls Module:init/1 to figure out the restart strategy, maximum restart intensity, and child processes. And to guarantee a synchronized startup, start_link/2,3 does not return until Module:init/1 has returned and all child processes have been started. No half-baked startups here bestie, she waits.
- If
SupName={local,Name}, the supervisor is registered locally asNameusingregister/2. - If
SupName={global,Name}, the supervisor is registered globally asNameusingglobal:register_name/2. - If
SupName={via,Module,Name}, the supervisor is registered asNameusing the registry represented byModule. ThatModulecallback has to export the functionsregister_name/2,unregister_name/1, andsend/2, and they must behave like the corresponding functions inglobal. So yeah,{via,global,Name}is a valid reference.
Module is the name of the callback module.
Args is any term that gets passed as the argument to Module:init/1.
- If the supervisor and all its child processes get created successfully (meaning every child process start function returns
{ok,Child},{ok,Child,Info}, orignore), the function returns{ok,Pid}, wherePidis the pid of the supervisor. β¨ - If a process with the specified
SupNameis already out there, the function returns{error,{already_started,Pid}}, wherePidis that process's pid. Sorry girl, name's taken π - If
Module:init/1returnsignore, this function returnsignoretoo, and the supervisor terminates with reasonnormal. - If
Module:init/1fails or returns an incorrect value, this function returns{error,Term}, whereTermis a term with info about the error, and the supervisor terminates with reasonTerm. - If any child process start function fails, or returns an error tuple, or an erroneous value β the supervisor first terminates all already-started child processes with reason
shutdown, then terminates itself and returns{error, {shutdown, Reason}}. Clean exit, no loose ends. π
stop(SupRef :: sup_ref(), Reason :: term(), Timeout :: timeout()) ->
ok.
Stop a supervisor. Just... stop her π
Orders the supervisor specified by SupRef to exit with the specified Reason, and waits for it to terminate. The supervisor will terminate all its children before exiting β she's not leaving anybody behind, that's her family.
The function returns ok if the supervisor terminates with the expected reason. Any reason other than normal, shutdown, or {shutdown,Term} causes an error report to be issued using logger. Oh and an exit signal with the same reason gets sent to linked processes and ports. Chain reaction, iykyk.
Timeout is an integer specifying how many milliseconds to wait for the supervisor to terminate, or the atom infinity to wait forever (well, indefinitely). If the supervisor hasn't terminated within the specified time, the call exits the calling process with reason timeout.
If the process doesn't exist, the call exits the calling process with reason noproc, or with reason {nodedown,Node} if the connection to the remote Node where the supervisor runs fails. π
WARNING: Calling this function from a (sub-)child process of the given supervisor will result in a deadlock which will last until either the shutdown timeout of the child or the timeout given to stop/3 has expired. Bestie do NOT do this, it's giving stuck-in-traffic-forever ππ¨
terminate_child(SupRef, Id) -> Result
when
SupRef :: sup_ref(),
Id :: pid() | child_id(),
Result :: ok | {error, Error},
Error :: not_found | simple_one_for_one.
Tells supervisor SupRef to terminate the specified child. Bye girl, we loved having you πΈ
If the supervisor is not simple_one_for_one, Id must be the child specification identifier. The process (if there is one) gets terminated, and unless it's a temporary child, the child specification stays kept by the supervisor. The child process can later be restarted by the supervisor. It can also be restarted explicitly by calling restart_child/2. And if you wanna remove the child specification entirely, use delete_child/2. Choose your fighter π
If the child is temporary, the child specification gets deleted as soon as the process terminates. Which means delete_child/2 has no meaning here, and restart_child/2 can't be used for these children. Temporary means temporary, periodt.
If the supervisor is simple_one_for_one, Id must be the pid/0 of the child process. If the specified process is alive but is not a child of the specified supervisor, the function returns {error,not_found}. And if you hand it the child specification identifier instead of a pid/0, the function returns {error,simple_one_for_one}. Wrong Id, wrong vibe check.
If successful, the function returns ok. If there's no child specification with the specified Id, the function returns {error,not_found}. π
which_child(SupRef, Id) -> Result
when
SupRef :: sup_ref(),
Id :: pid() | child_id(),
Result ::
{ok, {Id, Child, Type, Modules}} |
{error, Error},
Child :: child() | restarting,
Type :: worker(),
Modules :: modules(),
Error :: not_found.
Returns info about the child specification and child process identified by the given Id. It's giving gossip, but make it technical.
See which_children/1 for an explanation of the information returned.
If no child with the given Id exists, returns {error, not_found}.
which_children(SupRef) -> [{Id, Child, Type, Modules}]
when
SupRef :: sup_ref(),
Id :: child_id() | undefined,
Child :: child() | restarting,
Type :: worker(),
Modules :: modules().
Returns a newly created list with info about all child specifications and child processes belonging to supervisor SupRef. The full roster, the whole group chat π
Notice that calling this function when supervising many children under low memory conditions can cause an out of memory exception. Be careful bestie, this one can get heavy π
Here's the tea given for each child specification/process:
Id
As defined in the child specification, or undefined for a simple_one_for_one supervisor.
Childβ thepidof the matching child process, or the atomrestartingif that process is literally about to be restarted, orundefinedif there's no such process at all. three options, that's the whole menu πTypeβ as defined in the child specification. (yes bestie, straight from the spec, we don't make it up here)Modulesβ as defined in the child specification. same energy, same source π«Ά
π CALLBACKS π
init(Args)
okay so whenever a supervisor gets started with start_link/2,3, this function is what the shiny new process calls to figure out the whole situation: the restart strategy, the maximum restart intensity, and the child specifications. it's giving orientation day β¨
Args is literally the Args argument you passed into the start function. that's it, that's the tea. π
SupFlags is the supervisor flags, and they define the restart strategy plus the maximum restart intensity for this supervisor. [ChildSpec] is a list of valid child specifications, and those are what spell out which child processes the supervisor has to start and monitor. want the full lore? it's all in the discussion in section Supervision Principles earlier.
π¨ notice, bestie: when the restart strategy issimple_one_for_one, that list of child specifications has to be a list with one child specification only. (the child specification identifier? ignored. we said what we said.) no child process gets started during the initialization phase at all β every single child is assumed to be started dynamically usingstart_child/2. dynamic queen behavior fr fr π
also, this function can return ignore. just throwing that out there π
β οΈ another notice: this function can also be called as a part of a code upgrade procedure. which means it is NOT to have any side effects. seriously. keep it pure, keep it clean. for more info about code upgrade of supervisors, head to section Changing a Supervisor in OTP Design Principles. iykyk π§
and that's the whole page, bestie!! ππ big thanks to Ericsson AB for this one β it's the supervisor(3) page from stdlib 8.0.4, and she is iconic, no cap. go build something that doesn't crash. slay πβ¨
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms