%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2010-2011. 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(diameter_util). %% %% Utility functions. %% -export([consult/2, run/1, fold/3, foldl/3, scramble/1, ps/0]). -define(L, atom_to_list). %% consult/2 %% %% Extract info from the app/appup file (presumably) of the named %% application. consult(Name, Suf) when is_atom(Name), is_atom(Suf) -> case code:lib_dir(Name, ebin) of {error = E, Reason} -> {E, {Name, Reason}}; Dir -> consult(filename:join([Dir, ?L(Name) ++ "." ++ ?L(Suf)])) end. consult(Path) -> case file:consult(Path) of {ok, Terms} -> Terms; {error, Reason} -> {error, {Path, Reason}} end. %% Name/Path in the return value distinguish the errors and allow for %% a useful badmatch. %% run/1 %% %% Evaluate functions in parallel and return a list of those that %% failed to return. The fun takes a boolean (did the function return %% or not), the function that was evaluated, the return value or exit %% reason and the prevailing accumulator. run(L) -> fold(fun cons/4, [], L). cons(true, _, _, Acc) -> Acc; cons(false, F, RC, Acc) -> [{F, RC} | Acc]. %% fold/3 %% %% Parallel fold. Results are folded in the order received. fold(Fun, Acc0, L) when is_function(Fun, 4) -> Ref = make_ref(), %% Spawn a middleman to collect down messages from processes %% spawned for each function so as not to assume that all DOWN %% messages are ours. MRef = run1([fun fold/4, Ref, Fun, Acc0, L], Ref), {Ref, RC} = down(MRef), RC. fold(Ref, Fun, Acc0, L) -> recv(run(Ref, L), Ref, Fun, Acc0). run(Ref, L) -> [{run1(F, Ref), F} || F <- L]. run1(F, Ref) -> {_, MRef} = spawn_monitor(fun() -> exit({Ref, eval(F)}) end), MRef. recv([], _, _, Acc) -> Acc; recv(L, Ref, Fun, Acc) -> {MRef, R} = down(), {MRef, F} = lists:keyfind(MRef, 1, L), recv(lists:keydelete(MRef, 1, L), Ref, Fun, acc(R, Ref, F, Fun, Acc)). acc({Ref, RC}, Ref, F, Fun, Acc) -> Fun(true, F, RC, Acc); acc(Reason, _, F, Fun, Acc) -> Fun(false, F, Reason, Acc). down(MRef) -> receive {'DOWN', MRef, process, _, Reason} -> Reason end. down() -> receive {'DOWN', MRef, process, _, Reason} -> {MRef, Reason} end. %% foldl/3 %% %% Parallel fold. Results are folded in order of the function list. foldl(Fun, Acc0, L) when is_function(Fun, 4) -> Ref = make_ref(), recvl(run(Ref, L), Ref, Fun, Acc0). recvl([], _, _, Acc) -> Acc; recvl([{MRef, F} | L], Ref, Fun, Acc) -> R = down(MRef), recvl(L, Ref, Fun, acc(R, Ref, F, Fun, Acc)). %% scramble/1 %% %% Sort a list into random order. scramble(L) -> foldl(fun(true, _, S, false) -> S end, false, [[fun s/1, L]]). s(L) -> random:seed(now()), s([], L). s(Acc, []) -> Acc; s(Acc, L) -> {H, [T|Rest]} = lists:split(random:uniform(length(L)) - 1, L), s([T|Acc], H ++ Rest). %% ps/0 ps() -> [{P, process_info(P)} || P <- erlang:processes()]. %% eval/1 eval({M,[F|A]}) when is_atom(F) -> apply(M,F,A); eval({M,F,A}) -> apply(M,F,A); eval([F|A]) when is_function(F) -> apply(F,A); eval(L) when is_list(L) -> run(L); eval(F) when is_function(F,0) -> F().