Packages

Elixir-inspired standard library modules for Erlang

Current section

Files

Jump to
ex_stdlib src process.erl
Raw

src/process.erl

%%% @doc
%%% Process utilities module inspired by Elixir's Process module.
%%%
%%% This module provides convenient functions for working with processes,
%%% building on top of Erlang's built-in process functionality.
%%% @end
-module(process).
-compile({no_auto_import,[whereis/1,demonitor/2]}).
%% API
-export([alive/1, monitor/1, demonitor/1, demonitor/2]).
-export([flag/2, info/0, info/1, info/2]).
-export([sleep/1, exit/2, cancel_timer/1]).
-export([send_after/3, send_interval/3]).
-export([group_leader/0, group_leader/2]).
-export([get/0, get/1, put/2, get_keys/0, get_keys/1, erase/0, erase/1]).
-export([whereis/1, register/2, unregister/1, registered/0]).
%% Types
-type process_ref() :: pid() | atom() | {atom(), node()}.
-type process_flag() :: trap_exit | error_handler | min_heap_size |
min_bin_vheap_size | priority | save_calls |
sensitive | max_heap_size.
-type process_info_item() :: registered_name | status | links | monitors |
monitored_by | trap_exit | error_handler |
priority | group_leader | total_heap_size |
heap_size | stack_size | reductions |
garbage_collection | suspending | current_function |
current_location | current_stacktrace |
initial_call | dictionary | message_queue_len |
messages | binary | memory.
-type monitor_option() :: {alias, reply_demonitor | explicit_unalias}.
-export_type([process_ref/0, process_flag/0, process_info_item/0, monitor_option/0]).
%%% @doc
%%% Returns true if the process is alive, false otherwise.
%%%
%%% This is equivalent to is_process_alive/1 but with a more convenient name.
%%% @end
-spec alive(process_ref()) -> boolean().
alive(Pid) when is_pid(Pid) ->
is_process_alive(Pid);
alive(Name) when is_atom(Name) ->
case whereis(Name) of
undefined -> false;
Pid -> is_process_alive(Pid)
end;
alive({Name, Node}) when is_atom(Name), is_atom(Node) ->
case rpc:call(Node, erlang, whereis, [Name]) of
{badrpc, _} -> false;
undefined -> false;
Pid -> rpc:call(Node, erlang, is_process_alive, [Pid])
end.
%%% @doc
%%% Monitors the given process and returns the monitor reference.
%%%
%%% This is a convenience wrapper around erlang:monitor/2.
%%% @end
-spec monitor(process_ref()) -> reference().
monitor(Process) ->
erlang:monitor(process, Process).
%%% @doc
%%% Removes the monitor identified by the given reference.
%%%
%%% This is equivalent to demonitor(MonitorRef, [flush]).
%%% @end
-spec demonitor(reference()) -> true.
demonitor(MonitorRef) ->
demonitor(MonitorRef, [flush]).
%%% @doc
%%% Removes the monitor identified by the given reference with options.
%%%
%%% This is a convenience wrapper around erlang:demonitor/2.
%%% @end
-spec demonitor(reference(), [flush | info]) -> true.
demonitor(MonitorRef, Options) ->
erlang:demonitor(MonitorRef, Options).
%%% @doc
%%% Sets a process flag for the current process.
%%%
%%% This is a convenience wrapper around erlang:process_flag/2.
%%% @end
-spec flag(process_flag(), term()) -> term().
flag(Flag, Value) ->
erlang:process_flag(Flag, Value).
%%% @doc
%%% Returns information about the current process.
%%%
%%% This is equivalent to info(self()).
%%% @end
-spec info() -> [{process_info_item(), term()}].
info() ->
info(self()).
%%% @doc
%%% Returns information about the given process.
%%%
%%% Returns undefined if the process is not alive.
%%% @end
-spec info(process_ref()) -> [{process_info_item(), term()}] | undefined.
info(Process) ->
case resolve_process(Process) of
undefined -> undefined;
Pid -> erlang:process_info(Pid)
end.
%%% @doc
%%% Returns specific information about the given process.
%%%
%%% Returns undefined if the process is not alive or the info item is not available.
%%% @end
-spec info(process_ref(), process_info_item()) -> {process_info_item(), term()} | undefined.
info(Process, Item) ->
case resolve_process(Process) of
undefined -> undefined;
Pid -> erlang:process_info(Pid, Item)
end.
%%% @doc
%%% Sleeps the current process for the given number of milliseconds.
%%%
%%% This is a convenience wrapper around timer:sleep/1.
%%% @end
-spec sleep(non_neg_integer()) -> ok.
sleep(Timeout) ->
timer:sleep(Timeout).
%%% @doc
%%% Terminates the given process with the given reason.
%%%
%%% This is a convenience wrapper around erlang:exit/2.
%%% @end
-spec exit(process_ref(), term()) -> true.
exit(Process, Reason) ->
case resolve_process(Process) of
undefined -> true;
Pid -> erlang:exit(Pid, Reason)
end.
%%% @doc
%%% Cancels a timer created by send_after/3 or send_interval/3.
%%%
%%% This is a convenience wrapper around erlang:cancel_timer/1.
%%% @end
-spec cancel_timer(reference()) -> non_neg_integer() | false.
cancel_timer(TimerRef) ->
erlang:cancel_timer(TimerRef).
%%% @doc
%%% Sends a message to a process after the given time.
%%%
%%% This is a convenience wrapper around erlang:send_after/3.
%%% @end
-spec send_after(non_neg_integer(), process_ref(), term()) -> reference().
send_after(Time, Dest, Msg) ->
case resolve_process(Dest) of
undefined -> error({badarg, Dest});
Pid -> erlang:send_after(Time, Pid, Msg)
end.
%%% @doc
%%% Sends a message to a process repeatedly at the given interval.
%%%
%%% This is a convenience wrapper around timer:send_interval/3.
%%% @end
-spec send_interval(non_neg_integer(), process_ref(), term()) -> {ok, reference()} | {error, term()}.
send_interval(Interval, Dest, Msg) ->
case resolve_process(Dest) of
undefined -> error({badarg, Dest});
Pid -> timer:send_interval(Interval, Pid, Msg)
end.
%%% @doc
%%% Returns the group leader of the current process.
%%%
%%% This is a convenience wrapper around erlang:group_leader/0.
%%% @end
-spec group_leader() -> pid().
group_leader() ->
erlang:group_leader().
%%% @doc
%%% Sets the group leader of the given process.
%%%
%%% This is a convenience wrapper around erlang:group_leader/2.
%%% @end
-spec group_leader(pid(), process_ref()) -> true.
group_leader(GroupLeader, Process) ->
case resolve_process(Process) of
undefined -> error({badarg, Process});
Pid -> erlang:group_leader(GroupLeader, Pid)
end.
%%% @doc
%%% Returns the process dictionary of the current process.
%%%
%%% This is a convenience wrapper around erlang:get/0.
%%% @end
-spec get() -> [{term(), term()}].
get() ->
erlang:get().
%%% @doc
%%% Returns the value associated with the given key in the process dictionary.
%%%
%%% This is a convenience wrapper around erlang:get/1.
%%% @end
-spec get(term()) -> term() | undefined.
get(Key) ->
erlang:get(Key).
%%% @doc
%%% Stores a key-value pair in the process dictionary.
%%%
%%% This is a convenience wrapper around erlang:put/2.
%%% @end
-spec put(term(), term()) -> term() | undefined.
put(Key, Value) ->
erlang:put(Key, Value).
%%% @doc
%%% Returns all keys in the process dictionary.
%%%
%%% This is a convenience wrapper around erlang:get_keys/0.
%%% @end
-spec get_keys() -> [term()].
get_keys() ->
erlang:get_keys().
%%% @doc
%%% Returns all keys associated with the given value in the process dictionary.
%%%
%%% This is a convenience wrapper around erlang:get_keys/1.
%%% @end
-spec get_keys(term()) -> [term()].
get_keys(Value) ->
erlang:get_keys(Value).
%%% @doc
%%% Erases the entire process dictionary.
%%%
%%% This is a convenience wrapper around erlang:erase/0.
%%% @end
-spec erase() -> [{term(), term()}].
erase() ->
erlang:erase().
%%% @doc
%%% Erases the given key from the process dictionary.
%%%
%%% This is a convenience wrapper around erlang:erase/1.
%%% @end
-spec erase(term()) -> term() | undefined.
erase(Key) ->
erlang:erase(Key).
%%% @doc
%%% Returns the PID of the process registered under the given name.
%%%
%%% This is a convenience wrapper around erlang:whereis/1.
%%% @end
-spec whereis(atom()) -> pid() | undefined.
whereis(Name) ->
erlang:whereis(Name).
%%% @doc
%%% Registers the current process under the given name.
%%%
%%% This is a convenience wrapper around erlang:register/2.
%%% @end
-spec register(atom(), process_ref()) -> true.
register(Name, Process) ->
case resolve_process(Process) of
undefined -> error({badarg, Process});
Pid -> erlang:register(Name, Pid)
end.
%%% @doc
%%% Unregisters the given name.
%%%
%%% This is a convenience wrapper around erlang:unregister/1.
%%% @end
-spec unregister(atom()) -> true.
unregister(Name) ->
erlang:unregister(Name).
%%% @doc
%%% Returns a list of all registered process names.
%%%
%%% This is a convenience wrapper around erlang:registered/0.
%%% @end
-spec registered() -> [atom()].
registered() ->
erlang:registered().
%%%=============================================================================
%%% Internal functions
%%%=============================================================================
%% @private
%% Resolves a process reference to a PID
-spec resolve_process(process_ref()) -> pid() | undefined.
resolve_process(Pid) when is_pid(Pid) ->
case is_process_alive(Pid) of
true -> Pid;
false -> undefined
end;
resolve_process(Name) when is_atom(Name) ->
whereis(Name);
resolve_process({Name, Node}) when is_atom(Name), is_atom(Node) ->
case rpc:call(Node, erlang, whereis, [Name]) of
{badrpc, _} -> undefined;
Pid -> Pid
end.