proc_lib(3erl)
πΈ proc_lib β Erlang Module Definition πΈ
okay bestie, welcome to proc_lib, the Erlang module that's literally the backstage crew for every OTP process you've ever loved. It's the 3erl manual page, and she's a long one, so buckle up π
β¨ NAME β¨
proc_lib β functions for asynchronous and synchronous start of processes adhering to the OTP design principles. that's the whole vibe. π
π DESCRIPTION π
So this module is what you use to start processes that adhere to the OTP Design Principles. Specifically? The functions in here are the ones the OTP standard behaviors (like gen_server and gen_statem, yes those icons) call when they're starting new processes. But you can also use them yourself to start "special processes" β user-defined processes that still comply with the OTP design principles. For an example, go look at section sys and proc_lib in OTP Design Principles. iykyk π¦
Some genuinely useful info gets initialized the moment a process starts. The registered names (or the process identifiers) of the parent process, plus the parent ancestors, are all stored together with information about the function that was initially called in the process. it's giving β¨*receipts*β¨
So here's a thing that trips people up, and ngl it's important: in "plain Erlang", a process is only said to terminate normally for exit reason normal. But a process started using proc_lib is also said to terminate normally if it exits with reason shutdown or {shutdown,Term}. And shutdown is the reason used when an application (read: supervision tree) is stopped. so yes, those count as normal now, slay π
When a process started using proc_lib terminates abnormally β meaning any other exit reason than normal, shutdown, or {shutdown,Term} β a crash report is generated, and it gets written to the terminal by the default logger handler that Kernel sets up. Want the lore on how crash reports were logged prior to Erlang/OTP 21.0? See SASL Error Logging in the SASL User's Guide. π
And unlike in "plain Erlang", proc_lib processes will not generate error reports β the ones the emulator writes to the terminal. Nope. All exceptions are converted to exits, which are ignored by the default logger handler. π«Ά
The crash report contains all that previously stored info β the ancestors, the initial function β plus the termination reason, plus information about other processes that terminate as a result of this process terminating. it's the full group chat screenshot, basically π
π§ DATA TYPES π§
spawn_option() = erlang:spawn_opt_option()
See erlang:spawn_opt/2,3,4,5. that's it, that's the whole description π
start_spawn_option()
start_spawn_option() =
link |
{priority, erlang:priority_level()} |
{fullsweep_after, integer() >= 0} |
{min_heap_size, integer() >= 0} |
{min_bin_vheap_size, integer() >= 0} |
{max_heap_size, erlang:max_heap_size()} |
{message_queue_data, erlang:message_queue_data()}
A restricted set of spawn options. Most notably, monitor is not part of these options. she's not invited to this party π
dict_or_pid()
dict_or_pid() =
pid() |
(ProcInfo :: [term()]) |
{X :: integer(), Y :: integer(), Z :: integer()}
three ways to identify a process, pick your fighter π
π EXPORTS π
format(CrashReport) -> string()
Types:
CrashReport = [term()]
Equivalent to format(CrashReport, latin1). short and sweet, periodt β¨
format(CrashReport, Encoding) -> string()
Types:
CrashReport = [term()]
Encoding = latin1 | unicode | utf8
Note: this function is deprecated in the sense that theerror_loggeris no longer the preferred interface for logging in Erlang/OTP. A new logging API was added in Erlang/OTP 21.0, but legacyerror_loggerhandlers can still be used. New Logger handlers do not need to use this function, since the formatting callback (report_cb) is included as metadata in the log event.
Okay so what does it actually do? A user-defined legacy error_logger event handler can use this function to format a crash report. The crash report is sent using logger(3erl), and the event to be handled is of the format {error_report, GL, {Pid, crash_report, CrashReport}}, where GL is the group leader pid of process Pid that sent the crash report. π
format(CrashReport, Encoding, Depth) -> string()
Types:
CrashReport = [term()]
Encoding = latin1 | unicode | utf8
Depth = unlimited | integer() >= 1
Note: this function is deprecated in the sense that theerror_loggeris no longer the preferred interface for logging in Erlang/OTP. A new logging API was added in Erlang/OTP 21.0, but legacyerror_loggerhandlers can still be used. New Logger handlers do not need to use this function, since the formatting callback (report_cb) is included as metadata in the log event.
Same deal again β a user-defined legacy error_logger event handler can use this one to format a crash report. And here's the cute part: when Depth is specified as a positive integer, it's used in the format string to limit the output like this: io_lib:format("~P", [Term,Depth]). cut off the extra detail, keep the vibe π₯
hibernate(Module, Function, Args) -> no_return()
Types:
Module = module()
Function = atom()
Args = [term()]
This does the same thing as the hibernate/3 BIF (and yes, it does actually call it), but it makes sure that exception handling and logging continue to work as expected when the process wakes up. π΄βπ
Always use this function instead of the BIF for processes started using proc_lib functions. like, don't be brave, bestie. just use this one. π
init_ack(Ret) -> ok
init_ack(Parent, Ret) -> ok
Types:
Parent = pid()
Ret = term()
This function must be used by a process that has been started by a start[_link]/3,4,5 function. It tells Parent that the process has initialized itself, has started, or has failed to initialize itself. it's the "hey mom i'm alive" text π«Ά
Function init_ack/1 uses the parent value previously stored by the start function used.
If this function is not called, the start function returns an error tuple (if a link and/or a time-out is used) or hangs otherwise. no cap, it will just sit there. π
The following example illustrates how this function and proc_lib:start_link/3 are used:
-module(my_proc).
-export([start_link/0]).
-export([init/1]).
start_link() ->
proc_lib:start_link(my_proc, init, [self()]).
init(Parent) ->
case do_initialization() of
ok ->
proc_lib:init_ack(Parent, {ok, self()});
{error, Reason} ->
exit(Reason)
end,
loop().
...
initial_call(Process) -> {Module, Function, Args} | false
Types:
Process = dict_or_pid()
Module = module()
Function = atom()
Args = [atom()]
Extracts the initial call of a process that was started using one of the spawn or start functions in this module. Process can be a pid, an integer tuple (from which a pid can be created), or the process information of a process Pid fetched through an erlang:process_info(Pid) function call. detective work, but make it Erlang π
Note: the listArgsno longer contains the arguments, but the same number of atoms as the number of arguments; the first atom is'Argument__1', the second'Argument__2', and so on. The reason is that the argument list could waste a significant amount of memory, and if the argument list contained funs, it could be impossible to upgrade the code for the module.
And if the process was spawned using a fun, initial_call/1 no longer returns the fun, but the module, the function for the local function implementing the fun, and the arity β for example, {some_module,-work/3-fun-0-,0}, which means the fun was created in function some_module:work/3. Why? Because keeping the fun would prevent code upgrade for the module, and a significant amount of memory could be wasted. iconic behaviour, honestly π
spawn(Fun) -> pid()
spawn(Node, Fun) -> pid()
spawn(Module, Function, Args) -> pid()
spawn(Node, Module, Function, Args) -> pid()
Types:
Node = node()
Fun = function()
Module = module()
Function = atom()
Args = [term()]
Spawns a new process and initializes it as described at the beginning of this manual page. The process is spawned using the spawn BIFs. classic β¨
spawn_link(Fun) -> pid()
spawn_link(Node, Fun) -> pid()
spawn_link(Module, Function, Args) -> pid()
spawn_link(Node, Module, Function, Args) -> pid()
Types:
Node = node()
Fun = function()
Module = module()
Function = atom()
Args = [term()]
Spawns a new process and initializes it as described at the beginning of this manual page. This time it's spawned using the spawn_link BIFs. now you're linked, no take-backs π
spawn_opt(Fun, SpawnOpts) -> pid() | {pid(), reference()}
spawn_opt(Node, Function, SpawnOpts) -> pid() | {pid(), reference()}
spawn_opt(Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()}
spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()}
Types:
Node = node()
Fun = function()
Module = module()
Function = atom()
Args = [term()]
SpawnOpts = [spawn_option()]
Spawns a new process and initializes it as described at the beginning of this manual page. Spawned using the erlang:spawn_opt BIFs. all the options, all the power π
start(Module, Function, Args) -> Ret
start(Module, Function, Args, Time) -> Ret
start(Module, Function, Args, Time, SpawnOpts) -> Ret
Types:
Module = module()
Function = atom()
Args = [term()]
Time = timeout()
SpawnOpts = [start_spawn_option()]
Ret = term() | {error, Reason :: term()}
Starts a new process synchronously β it spawns the process and then waits for it to actually start. When the process has started, it must call init_ack(Parent, Ret) or init_ack(Ret), where Parent is the process that evaluates this function. And at that moment, Ret is returned. teamwork, baby π«±π«²
If Time is specified as an integer, this function waits Time milliseconds for the new process to call init_ack, or else Ret = {error, timeout} will be returned and the process is killed. dramatic but fair π
Argument SpawnOpts, if specified, is passed as the last argument to the spawn_opt/2,3,4,5 BIF.
Note: using spawn optionmonitoris not allowed. It causes the function to fail with reasonbadarg. don't try it, she will not be nice about it π
start_link(Module, Function, Args) -> Ret
start_link(Module, Function, Args, Time) -> Ret
start_link(Module, Function, Args, Time, SpawnOpts) -> Ret
Types:
okay so the types for this one are rolling in with part 2 β she's a big section, hold that thought bestie π
Module = module()
Function = atom()
Args = [term()]
Time = timeout()
SpawnOpts = [start_spawn_option()]
Ret = term() | {error, Reason :: term()}
okay so this is the classic one π
it starts a whole new process synchronously β meaning it spawns the thing and then waits for it to actually get going. A link gets atomically set on the shiny new process (atomic link = no lil gap where bad stuff can slip through, we love to see it). Then, once the process has started, it must call init_ack(Parent, Ret) or init_ack(Ret), where Parent is the process that evaluated this function. That's the moment Ret gets returned to you. π
If Time is given as an integer, this function waits Time milliseconds for the new process to call init_ack, or you get Ret = {error, timeout} and the process gets killed. ghosted AND deleted, iconic π
If the process crashes before it ever calls init_ack/1,2, you get Ret = {error, Reason} β but only if the calling process traps exits. fr fr, trap your exits.
Argument SpawnOpts, if you pass it, is passed as the last argument to the spawn_opt/2,3,4,5 BIF.
Note: using spawn optionmonitoris not allowed. It makes the function fail with reasonbadarg. don't do it bestie π
start_monitor(Module, Function, Args) -> {Ret, Mon}
start_monitor(Module, Function, Args, Time) -> {Ret, Mon}
start_monitor(Module, Function, Args, Time, SpawnOpts) -> {Ret, Mon}
Types:
Module = module()
Function = atom()
Args = [term()]
Time = timeout()
SpawnOpts = [start_spawn_option()]
Mon = reference()
Ret = term() | {error, Reason :: term()}
Same synchronous energy, but this time it's a monitor that gets atomically set on the newly spawned process instead of a link. π¦ Spawns, then waits for it to start. When it's up, the process must call init_ack(Parent, Ret) or init_ack(Ret), where Parent is the process that evaluated this function β and that's when Ret comes back to you.
If Time is an integer, you wait Time milliseconds for that init_ack call, or Ret = {error, timeout} and the process is killed. β
The return value is {Ret, Mon}, where Ret is the same Ret you passed to init_ack(), and Mon is the monitor reference of the monitor that got set up. π
A 'DOWN' message will be delivered to the caller if this function returns and the spawned process terminates. That's true also when the operation times out β so yeah, expect your 'DOWN' even in the timeout case, no cap.
Argument SpawnOpts, if specified, is passed as the last argument to the spawn_opt/2,3,4,5 BIF.
Note: using spawn optionmonitoris not allowed here either. Fails with reasonbadarg. π
stop(Process) -> ok
Types:
Process = pid() | RegName | {RegName, node()}
Exactly equivalent to stop(Process, normal, infinity). that's it, that's the whole vibe β¨
stop(Process, Reason, Timeout) -> ok
Types:
Process = pid() | RegName | {RegName, node()}
Reason = term()
Timeout = timeout()
Orders the process to exit with the specified Reason and waits for it to actually terminate. polite but firm π«Ά
Returns ok if the process exits with the specified Reason within Timeout milliseconds.
- If the call times out, a
timeoutexception is raised. - If the process doesn't exist, a
noprocexception is raised.
The implementation is based on theterminatesystem message, and it requires that the process handles system messages correctly. So like, if the process isn't playing nice with system messages, this is not gonna go how you want π For the whole system-message briefing, seesys(3erl)and the section sys and proc_lib in OTP Design Principles.
translate_initial_call(Process) -> {Module, Function, Arity}
Types:
Process = dict_or_pid()
Module = module()
Function = atom()
Arity = byte()
This one's the backstage-pass function π₯ β it's used by c:i/0 and c:regs/0 to present process info all nicely.
What it does: it extracts the initial call of a process that was started using one of the spawn or start functions in this module, and translates it into actually useful information. Process can be a pid, an integer tuple (from which a pid can be created), or the process information of a process Pid fetched through an erlang:process_info(Pid) function call. flexible queen π
If the initial call is to one of the system-defined behaviors like gen_server or gen_event, it gets translated into more useful info. If a gen_server is spawned, the returned Module is the name of the callback module and Function is init (the function that kicks off the new server).
A supervisor and a supervisor_bridge are also gen_server processes, bestie β plot twist π So to return info saying "hey this is a supervisor" plus the callback module name, Module is supervisor and Function is the name of the supervisor callback module. Arity is 1, since init/1 is called first in the callback module.
By default, {proc_lib,init_p,5} is returned if no info about the initial call can be found. It's assumed that the caller knows the process was spawned with the proc_lib module. iykyk π
π SEE ALSO π
okay that's the whole page bestie, go forth and spawn responsibly π§ββοΈπ this lil masterpiece is brought to you by Ericsson AB, living in stdlib 4.3.1.3, filed under proc_lib(3erl) β mwah, stan a stable stdlib π πβ¨πΈ
π¦ where this page comes from
- ubuntu noble: erlang-manpages 1:25.3.2.8+dfsg-1ubuntu4 Β· Apache-2.0
- ubuntu noble: erlang-manpages 1:25.3.2.8+dfsg-1ubuntu4.8 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms