aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded/src/erlang.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded/src/erlang.erl')
-rw-r--r--erts/preloaded/src/erlang.erl683
1 files changed, 683 insertions, 0 deletions
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
new file mode 100644
index 0000000000..6f92b319b7
--- /dev/null
+++ b/erts/preloaded/src/erlang.erl
@@ -0,0 +1,683 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erlang).
+
+-export([apply/2,apply/3,spawn/4,spawn_link/4,
+ spawn_monitor/1,spawn_monitor/3,
+ spawn_opt/2,spawn_opt/3,spawn_opt/4,spawn_opt/5,
+ disconnect_node/1]).
+-export([spawn/1, spawn_link/1, spawn/2, spawn_link/2]).
+-export([yield/0]).
+-export([crasher/6]).
+-export([fun_info/1]).
+-export([send_nosuspend/2, send_nosuspend/3]).
+-export([localtime_to_universaltime/1]).
+-export([suspend_process/1]).
+-export([min/2,max/2]).
+
+-export([dlink/1, dunlink/1, dsend/2, dsend/3, dgroup_leader/2,
+ dexit/2, dmonitor_node/3, dmonitor_p/2]).
+
+-export([delay_trap/2]).
+
+-export([set_cookie/2, get_cookie/0]).
+
+-export([nodes/0]).
+
+-export([concat_binary/1]).
+
+-export([list_to_integer/2,integer_to_list/2]).
+
+-export([flush_monitor_message/2]).
+
+-export([set_cpu_topology/1, format_cpu_topology/1]).
+
+-export([await_proc_exit/3]).
+
+-deprecated([hash/2]).
+
+-compile(nowarn_bif_clash).
+
+apply(Fun, Args) ->
+ apply(Fun, Args).
+
+apply(Mod, Name, Args) ->
+ apply(Mod, Name, Args).
+
+
+% Spawns with a fun
+spawn(F) when is_function(F) ->
+ spawn(erlang, apply, [F, []]);
+spawn({M,F}=MF) when is_atom(M), is_atom(F) ->
+ spawn(erlang, apply, [MF, []]);
+spawn(F) ->
+ erlang:error(badarg, [F]).
+
+spawn(N, F) when N =:= node() ->
+ spawn(F);
+spawn(N, F) when is_function(F) ->
+ spawn(N, erlang, apply, [F, []]);
+spawn(N, {M,F}=MF) when is_atom(M), is_atom(F) ->
+ spawn(N, erlang, apply, [MF, []]);
+spawn(N, F) ->
+ erlang:error(badarg, [N, F]).
+
+spawn_link(F) when is_function(F) ->
+ spawn_link(erlang, apply, [F, []]);
+spawn_link({M,F}=MF) when is_atom(M), is_atom(F) ->
+ spawn_link(erlang, apply, [MF, []]);
+spawn_link(F) ->
+ erlang:error(badarg, [F]).
+
+spawn_link(N, F) when N =:= node() ->
+ spawn_link(F);
+spawn_link(N, F) when is_function(F) ->
+ spawn_link(N, erlang, apply, [F, []]);
+spawn_link(N, {M,F}=MF) when is_atom(M), is_atom(F) ->
+ spawn_link(N, erlang, apply, [MF, []]);
+spawn_link(N, F) ->
+ erlang:error(badarg, [N, F]).
+
+%% Spawn and atomically set up a monitor.
+
+spawn_monitor(F) when is_function(F, 0) ->
+ erlang:spawn_opt({erlang,apply,[F,[]],[monitor]});
+spawn_monitor(F) ->
+ erlang:error(badarg, [F]).
+
+spawn_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
+ erlang:spawn_opt({M,F,A,[monitor]});
+spawn_monitor(M, F, A) ->
+ erlang:error(badarg, [M,F,A]).
+
+spawn_opt(F, O) when is_function(F) ->
+ spawn_opt(erlang, apply, [F, []], O);
+spawn_opt({M,F}=MF, O) when is_atom(M), is_atom(F) ->
+ spawn_opt(erlang, apply, [MF, []], O);
+spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility
+ spawn_opt(M, F, A, O);
+spawn_opt(F, O) ->
+ erlang:error(badarg, [F, O]).
+
+spawn_opt(N, F, O) when N =:= node() ->
+ spawn_opt(F, O);
+spawn_opt(N, F, O) when is_function(F) ->
+ spawn_opt(N, erlang, apply, [F, []], O);
+spawn_opt(N, {M,F}=MF, O) when is_atom(M), is_atom(F) ->
+ spawn_opt(N, erlang, apply, [MF, []], O);
+spawn_opt(N, F, O) ->
+ erlang:error(badarg, [N, F, O]).
+
+% Spawns with MFA
+
+spawn(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) ->
+ spawn(M,F,A);
+spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
+ case is_well_formed_list(A) of
+ true ->
+ ok;
+ false ->
+ erlang:error(badarg, [N, M, F, A])
+ end,
+ case catch gen_server:call({net_kernel,N},
+ {spawn,M,F,A,group_leader()},
+ infinity) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ Error ->
+ case remote_spawn_error(Error, {no_link, N, M, F, A, []}) of
+ {fault, Fault} ->
+ erlang:error(Fault, [N, M, F, A]);
+ Pid ->
+ Pid
+ end
+ end;
+spawn(N,M,F,A) ->
+ erlang:error(badarg, [N, M, F, A]).
+
+spawn_link(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) ->
+ spawn_link(M,F,A);
+spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
+ case is_well_formed_list(A) of
+ true ->
+ ok;
+ _ ->
+ erlang:error(badarg, [N, M, F, A])
+ end,
+ case catch gen_server:call({net_kernel,N},
+ {spawn_link,M,F,A,group_leader()},
+ infinity) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ Error ->
+ case remote_spawn_error(Error, {link, N, M, F, A, []}) of
+ {fault, Fault} ->
+ erlang:error(Fault, [N, M, F, A]);
+ Pid ->
+ Pid
+ end
+ end;
+spawn_link(N,M,F,A) ->
+ erlang:error(badarg, [N, M, F, A]).
+
+spawn_opt(M, F, A, Opts) ->
+ case catch erlang:spawn_opt({M,F,A,Opts}) of
+ {'EXIT',{Reason,_}} ->
+ erlang:error(Reason, [M,F,A,Opts]);
+ Res ->
+ Res
+ end.
+
+spawn_opt(N, M, F, A, O) when N =:= node(),
+ is_atom(M), is_atom(F), is_list(A),
+ is_list(O) ->
+ spawn_opt(M, F, A, O);
+spawn_opt(N, M, F, A, O) when is_atom(N), is_atom(M), is_atom(F) ->
+ case {is_well_formed_list(A), is_well_formed_list(O)} of
+ {true, true} ->
+ ok;
+ _ ->
+ erlang:error(badarg, [N, M, F, A, O])
+ end,
+ case lists:member(monitor, O) of
+ false -> ok;
+ true -> erlang:error(badarg, [N, M, F, A, O])
+ end,
+ {L,NO} = lists:foldl(fun (link, {_, NewOpts}) ->
+ {link, NewOpts};
+ (Opt, {LO, NewOpts}) ->
+ {LO, [Opt|NewOpts]}
+ end,
+ {no_link,[]},
+ O),
+ case catch gen_server:call({net_kernel,N},
+ {spawn_opt,M,F,A,NO,L,group_leader()},
+ infinity) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ Error ->
+ case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
+ {fault, Fault} ->
+ erlang:error(Fault, [N, M, F, A, O]);
+ Pid ->
+ Pid
+ end
+ end;
+spawn_opt(N,M,F,A,O) ->
+ erlang:error(badarg, [N,M,F,A,O]).
+
+remote_spawn_error({'EXIT', {{nodedown,N}, _}}, {L, N, M, F, A, O}) ->
+ {Opts, LL} = case L =:= link of
+ true ->
+ {[link|O], [link]};
+ false ->
+ {O, []}
+ end,
+ spawn_opt(erlang,crasher,[N,M,F,A,Opts,noconnection], LL);
+remote_spawn_error({'EXIT', {Reason, _}}, _) ->
+ {fault, Reason};
+remote_spawn_error({'EXIT', Reason}, _) ->
+ {fault, Reason};
+remote_spawn_error(Other, _) ->
+ {fault, Other}.
+
+is_well_formed_list([]) ->
+ true;
+is_well_formed_list([_|Rest]) ->
+ is_well_formed_list(Rest);
+is_well_formed_list(_) ->
+ false.
+
+crasher(Node,Mod,Fun,Args,[],Reason) ->
+ error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
+ [Mod,Fun,Args,Node]),
+ exit(Reason);
+crasher(Node,Mod,Fun,Args,Opts,Reason) ->
+ error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
+ [Mod,Fun,Args,Opts,Node]),
+ exit(Reason).
+
+yield() ->
+ erlang:yield().
+
+nodes() -> erlang:nodes(visible).
+
+disconnect_node(Node) -> net_kernel:disconnect(Node).
+
+fun_info(Fun) when is_function(Fun) ->
+ Keys = [type,env,arity,name,uniq,index,new_uniq,new_index,module,pid],
+ fun_info_1(Keys, Fun, []).
+
+fun_info_1([K|Ks], Fun, A) ->
+ case erlang:fun_info(Fun, K) of
+ {K,undefined} -> fun_info_1(Ks, Fun, A);
+ {K,_}=P -> fun_info_1(Ks, Fun, [P|A])
+ end;
+fun_info_1([], _, A) -> A.
+
+-type dst() :: pid() | port() | atom() | {atom(), node()}.
+
+-spec send_nosuspend(dst(), term()) -> boolean().
+send_nosuspend(Pid, Msg) ->
+ send_nosuspend(Pid, Msg, []).
+
+-spec send_nosuspend(dst(), term(), ['noconnect' | 'nosuspend']) -> boolean().
+send_nosuspend(Pid, Msg, Opts) ->
+ case erlang:send(Pid, Msg, [nosuspend|Opts]) of
+ ok -> true;
+ _ -> false
+ end.
+
+localtime_to_universaltime(Localtime) ->
+ erlang:localtime_to_universaltime(Localtime, undefined).
+
+suspend_process(P) ->
+ case catch erlang:suspend_process(P, []) of
+ {'EXIT', {Reason, _}} -> erlang:error(Reason, [P]);
+ {'EXIT', Reason} -> erlang:error(Reason, [P]);
+ Res -> Res
+ end.
+
+%%
+%% If the emulator wants to perform a distributed command and
+%% a connection is not established to the actual node the following
+%% functions is called in order to set up the connection and then
+%% reactivate the command.
+%%
+
+dlink(Pid) ->
+ case net_kernel:connect(node(Pid)) of
+ true -> link(Pid);
+ false -> erlang:dist_exit(self(), noconnection, Pid), true
+ end.
+
+%% Can this ever happen?
+dunlink(Pid) ->
+ case net_kernel:connect(node(Pid)) of
+ true -> unlink(Pid);
+ false -> true
+ end.
+
+dmonitor_node(Node, Flag, []) ->
+ case net_kernel:connect(Node) of
+ true -> erlang:monitor_node(Node, Flag, []);
+ false -> self() ! {nodedown, Node}, true
+ end;
+
+dmonitor_node(Node, Flag, Opts) ->
+ case lists:member(allow_passive_connect,Opts) of
+ true ->
+ case net_kernel:passive_cnct(Node) of
+ true -> erlang:monitor_node(Node, Flag, Opts);
+ false -> self() ! {nodedown, Node}, true
+ end;
+ _ ->
+ dmonitor_node(Node,Flag,[])
+ end.
+
+dgroup_leader(Leader, Pid) ->
+ case net_kernel:connect(node(Pid)) of
+ true -> group_leader(Leader, Pid);
+ false -> true %% bad arg ?
+ end.
+
+dexit(Pid, Reason) ->
+ case net_kernel:connect(node(Pid)) of
+ true -> exit(Pid, Reason);
+ false -> true
+ end.
+
+dsend(Pid, Msg) when is_pid(Pid) ->
+ case net_kernel:connect(node(Pid)) of
+ true -> erlang:send(Pid, Msg);
+ false -> Msg
+ end;
+dsend(Port, Msg) when is_port(Port) ->
+ case net_kernel:connect(node(Port)) of
+ true -> erlang:send(Port, Msg);
+ false -> Msg
+ end;
+dsend({Name, Node}, Msg) ->
+ case net_kernel:connect(Node) of
+ true -> erlang:send({Name,Node}, Msg);
+ false -> Msg;
+ ignored -> Msg % Not distributed.
+ end.
+
+dsend(Pid, Msg, Opts) when is_pid(Pid) ->
+ case net_kernel:connect(node(Pid)) of
+ true -> erlang:send(Pid, Msg, Opts);
+ false -> ok
+ end;
+dsend(Port, Msg, Opts) when is_port(Port) ->
+ case net_kernel:connect(node(Port)) of
+ true -> erlang:send(Port, Msg, Opts);
+ false -> ok
+ end;
+dsend({Name, Node}, Msg, Opts) ->
+ case net_kernel:connect(Node) of
+ true -> erlang:send({Name,Node}, Msg, Opts);
+ false -> ok;
+ ignored -> ok % Not distributed.
+ end.
+
+dmonitor_p(process, ProcSpec) ->
+ %% ProcSpec = pid() | {atom(),atom()}
+ %% ProcSpec CANNOT be an atom because a locally registered process
+ %% is never handled here.
+
+ Node = case ProcSpec of
+ {S,N} when is_atom(S), is_atom(N), N =/= node() -> N;
+ _ when is_pid(ProcSpec) -> node(ProcSpec)
+ end,
+ case net_kernel:connect(Node) of
+ true ->
+ erlang:monitor(process, ProcSpec);
+ false ->
+ Ref = make_ref(),
+ self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
+ Ref
+ end.
+
+%%
+%% Trap function used when modified timing has been enabled.
+%%
+
+delay_trap(Result, 0) -> erlang:yield(), Result;
+delay_trap(Result, Timeout) -> receive after Timeout -> Result end.
+
+%%
+%% The business with different in and out cookies represented
+%% everywhere is discarded.
+%% A node has a cookie, connections/messages to that node use that cookie.
+%% Messages to us use our cookie. IF we change our cookie, other nodes
+%% have to reflect that, which we cannot forsee.
+%%
+set_cookie(Node, C) when Node =/= nonode@nohost, is_atom(Node) ->
+ Res = case C of
+ _ when is_atom(C) ->
+ auth:set_cookie(Node, C);
+ {CI,CO} when is_atom(CI), is_atom(CO) ->
+ auth:set_cookie(Node, {CI, CO});
+ _ ->
+ error
+ end,
+ case Res of
+ error -> exit(badarg);
+ Other -> Other
+ end.
+
+get_cookie() ->
+ auth:get_cookie().
+
+concat_binary(List) ->
+ list_to_binary(List).
+
+integer_to_list(I, 10) ->
+ erlang:integer_to_list(I);
+integer_to_list(I, Base)
+ when is_integer(I), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 ->
+ if I < 0 ->
+ [$-|integer_to_list(-I, Base, [])];
+ true ->
+ integer_to_list(I, Base, [])
+ end;
+integer_to_list(I, Base) ->
+ erlang:error(badarg, [I, Base]).
+
+integer_to_list(I0, Base, R0) ->
+ D = I0 rem Base,
+ I1 = I0 div Base,
+ R1 = if D >= 10 ->
+ [D-10+$A|R0];
+ true ->
+ [D+$0|R0]
+ end,
+ if I1 =:= 0 ->
+ R1;
+ true ->
+ integer_to_list(I1, Base, R1)
+ end.
+
+
+
+list_to_integer(L, 10) ->
+ erlang:list_to_integer(L);
+list_to_integer(L, Base)
+ when is_list(L), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 ->
+ case list_to_integer_sign(L, Base) of
+ I when is_integer(I) ->
+ I;
+ Fault ->
+ erlang:error(Fault, [L,Base])
+ end;
+list_to_integer(L, Base) ->
+ erlang:error(badarg, [L,Base]).
+
+list_to_integer_sign([$-|[_|_]=L], Base) ->
+ case list_to_integer(L, Base, 0) of
+ I when is_integer(I) ->
+ -I;
+ I ->
+ I
+ end;
+list_to_integer_sign([$+|[_|_]=L], Base) ->
+ list_to_integer(L, Base, 0);
+list_to_integer_sign([_|_]=L, Base) ->
+ list_to_integer(L, Base, 0);
+list_to_integer_sign(_, _) ->
+ badarg.
+
+list_to_integer([D|L], Base, I)
+ when is_integer(D), D >= $0, D =< $9, D < Base+$0 ->
+ list_to_integer(L, Base, I*Base + D-$0);
+list_to_integer([D|L], Base, I)
+ when is_integer(D), D >= $A, D < Base+$A-10 ->
+ list_to_integer(L, Base, I*Base + D-$A+10);
+list_to_integer([D|L], Base, I)
+ when is_integer(D), D >= $a, D < Base+$a-10 ->
+ list_to_integer(L, Base, I*Base + D-$a+10);
+list_to_integer([], _, I) ->
+ I;
+list_to_integer(_, _, _) ->
+ badarg.
+
+%% erlang:flush_monitor_message/2 is for internal use only!
+%%
+%% erlang:demonitor(Ref, [flush]) traps to
+%% erlang:flush_monitor_message(Ref, Res) when
+%% it needs to flush a monitor message.
+flush_monitor_message(Ref, Res) when is_reference(Ref), is_atom(Res) ->
+ receive {_, Ref, _, _, _} -> ok after 0 -> ok end,
+ Res.
+
+-record(cpu, {node = -1,
+ processor = -1,
+ processor_node = -1,
+ core = -1,
+ thread = -1,
+ logical = -1}).
+
+%% erlang:set_cpu_topology/1 is for internal use only!
+%%
+%% erlang:system_flag(cpu_topology, CpuTopology) traps to
+%% erlang:set_cpu_topology(CpuTopology).
+set_cpu_topology(CpuTopology) ->
+ try format_cpu_topology(erlang:system_flag(internal_cpu_topology,
+ cput_e2i(CpuTopology)))
+ catch
+ Class:Exception when Class =/= error; Exception =/= internal_error ->
+ erlang:error(badarg, [CpuTopology])
+ end.
+
+cput_e2i_clvl({logical, _}, _PLvl) ->
+ #cpu.logical;
+cput_e2i_clvl([E | _], PLvl) ->
+ case element(1, E) of
+ node -> case PLvl of
+ 0 -> #cpu.node;
+ #cpu.processor -> #cpu.processor_node
+ end;
+ processor -> case PLvl of
+ 0 -> #cpu.node;
+ #cpu.node -> #cpu.processor
+ end;
+ core -> #cpu.core;
+ thread -> #cpu.thread
+ end.
+
+cput_e2i(undefined) ->
+ undefined;
+cput_e2i(E) ->
+ rvrs(cput_e2i(E, -1, -1, #cpu{}, 0, cput_e2i_clvl(E, 0), [])).
+
+cput_e2i([], _NId, _PId, _IS, _PLvl, _Lvl, Res) ->
+ Res;
+cput_e2i([E|Es], NId0, PId, IS, PLvl, Lvl, Res0) ->
+ case cput_e2i(E, NId0, PId, IS, PLvl, Lvl, Res0) of
+ [] ->
+ cput_e2i(Es, NId0, PId, IS, PLvl, Lvl, Res0);
+ [#cpu{node = N,
+ processor = P,
+ processor_node = PN} = CPU|_] = Res1 ->
+ NId1 = case N > PN of
+ true -> N;
+ false -> PN
+ end,
+ cput_e2i(Es, NId1, P, CPU, PLvl, Lvl, Res1)
+ end;
+cput_e2i({Tag, [], TagList}, Nid, PId, CPU, PLvl, Lvl, Res) ->
+ %% Currently [] is the only valid InfoList
+ cput_e2i({Tag, TagList}, Nid, PId, CPU, PLvl, Lvl, Res);
+cput_e2i({node, NL}, Nid0, PId, _CPU, 0, #cpu.node, Res) ->
+ Nid1 = Nid0+1,
+ Lvl = cput_e2i_clvl(NL, #cpu.node),
+ cput_e2i(NL, Nid1, PId, #cpu{node = Nid1}, #cpu.node, Lvl, Res);
+cput_e2i({processor, PL}, Nid, PId0, _CPU, 0, #cpu.node, Res) ->
+ PId1 = PId0+1,
+ Lvl = cput_e2i_clvl(PL, #cpu.processor),
+ cput_e2i(PL, Nid, PId1, #cpu{processor = PId1}, #cpu.processor, Lvl, Res);
+cput_e2i({processor, PL}, Nid, PId0, CPU, PLvl, CLvl, Res)
+ when PLvl < #cpu.processor, CLvl =< #cpu.processor ->
+ PId1 = PId0+1,
+ Lvl = cput_e2i_clvl(PL, #cpu.processor),
+ cput_e2i(PL, Nid, PId1, CPU#cpu{processor = PId1,
+ processor_node = -1,
+ core = -1,
+ thread = -1}, #cpu.processor, Lvl, Res);
+cput_e2i({node, NL}, Nid0, PId, CPU, #cpu.processor, #cpu.processor_node,
+ Res) ->
+ Nid1 = Nid0+1,
+ Lvl = cput_e2i_clvl(NL, #cpu.processor_node),
+ cput_e2i(NL, Nid1, PId, CPU#cpu{processor_node = Nid1},
+ #cpu.processor_node, Lvl, Res);
+cput_e2i({core, CL}, Nid, PId, #cpu{core = C0} = CPU, PLvl, #cpu.core, Res)
+ when PLvl < #cpu.core ->
+ Lvl = cput_e2i_clvl(CL, #cpu.core),
+ cput_e2i(CL, Nid, PId, CPU#cpu{core = C0+1, thread = -1}, #cpu.core, Lvl,
+ Res);
+cput_e2i({thread, TL}, Nid, PId, #cpu{thread = T0} = CPU, PLvl, #cpu.thread,
+ Res) when PLvl < #cpu.thread ->
+ Lvl = cput_e2i_clvl(TL, #cpu.thread),
+ cput_e2i(TL, Nid, PId, CPU#cpu{thread = T0+1}, #cpu.thread, Lvl, Res);
+cput_e2i({logical, ID}, _Nid, PId, #cpu{processor=P, core=C, thread=T} = CPU,
+ PLvl, #cpu.logical, Res)
+ when PLvl < #cpu.logical, is_integer(ID), 0 =< ID, ID < 65536 ->
+ [CPU#cpu{processor = case P of -1 -> PId+1; _ -> P end,
+ core = case C of -1 -> 0; _ -> C end,
+ thread = case T of -1 -> 0; _ -> T end,
+ logical = ID} | Res].
+
+%% erlang:format_cpu_topology/1 is for internal use only!
+%%
+%% erlang:system_info(cpu_topology),
+%% and erlang:system_info({cpu_topology, Which}) traps to
+%% erlang:format_cpu_topology(InternalCpuTopology).
+format_cpu_topology(InternalCpuTopology) ->
+ try cput_i2e(InternalCpuTopology)
+ catch _ : _ -> erlang:error(internal_error, [InternalCpuTopology])
+ end.
+
+
+cput_i2e(undefined) -> undefined;
+cput_i2e(Is) -> cput_i2e(Is, true, #cpu.node, cput_i2e_tag_map()).
+
+cput_i2e([], _Frst, _Lvl, _TM) ->
+ [];
+cput_i2e([#cpu{logical = LID}| _], _Frst, Lvl, _TM) when Lvl == #cpu.logical ->
+ {logical, LID};
+cput_i2e([#cpu{} = I | Is], Frst, Lvl, TM) ->
+ cput_i2e(element(Lvl, I), Frst, Is, [I], Lvl, TM).
+
+cput_i2e(V, Frst, [I | Is], SameV, Lvl, TM) when V =:= element(Lvl, I) ->
+ cput_i2e(V, Frst, Is, [I | SameV], Lvl, TM);
+cput_i2e(-1, true, [], SameV, Lvl, TM) ->
+ cput_i2e(rvrs(SameV), true, Lvl+1, TM);
+cput_i2e(_V, true, [], SameV, Lvl, TM) when Lvl =/= #cpu.processor,
+ Lvl =/= #cpu.processor_node ->
+ cput_i2e(rvrs(SameV), true, Lvl+1, TM);
+cput_i2e(-1, _Frst, Is, SameV, #cpu.node, TM) ->
+ cput_i2e(rvrs(SameV), true, #cpu.processor, TM)
+ ++ cput_i2e(Is, false, #cpu.node, TM);
+cput_i2e(_V, _Frst, Is, SameV, Lvl, TM) ->
+ [{cput_i2e_tag(Lvl, TM), cput_i2e(rvrs(SameV), true, Lvl+1, TM)}
+ | cput_i2e(Is, false, Lvl, TM)].
+
+cput_i2e_tag_map() -> list_to_tuple([cpu | record_info(fields, cpu)]).
+
+cput_i2e_tag(Lvl, TM) ->
+ case element(Lvl, TM) of processor_node -> node; Other -> Other end.
+
+rvrs([_] = L) -> L;
+rvrs(Xs) -> rvrs(Xs, []).
+
+rvrs([],Ys) -> Ys;
+rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]).
+
+%% erlang:await_proc_exit/3 is for internal use only!
+%%
+%% BIFs that need to await a specific process exit before
+%% returning traps to erlang:await_proc_exit/3.
+%%
+%% NOTE: This function is tightly coupled to
+%% the implementation of the
+%% erts_bif_prep_await_proc_exit_*()
+%% functions in bif.c. Do not make
+%% any changes to it without reading
+%% the comment about them in bif.c!
+await_proc_exit(Proc, Op, Data) ->
+ Mon = erlang:monitor(process, Proc),
+ receive
+ {'DOWN', Mon, process, _Proc, Reason} ->
+ case Op of
+ apply ->
+ {M, F, A} = Data,
+ erlang:apply(M, F, A);
+ data ->
+ Data;
+ reason ->
+ Reason
+ end
+ end.
+
+min(A, B) when A > B -> B;
+min(A, _) -> A.
+
+max(A, B) when A < B -> B;
+max(A, _) -> A.