-module(call_purged_fun_tester). -export([do/4]). %% Resurrect line macro when hipe compiled -ifdef(hipe). -define(line, put(the_line,?LINE),). do(Priv, Data, Type, Opts) -> try do_it(Priv, Data, Type, Opts) catch C:E -> ST = erlang:get_stacktrace(), io:format("Caught exception from line ~p:\n~p\n", [get(the_line), ST]), io:format("Message queue: ~p\n", [process_info(self(), messages)]), erlang:raise(C, E, ST) end. -else. -define(line,). do(P,D,T,O) -> do_it(P,D,T,O). -endif. do_it(Priv, Data, Type, Opts) -> File = filename:join(Data, "my_code_test2"), Code = filename:join(Priv, "my_code_test2"), catch erlang:purge_module(my_code_test2), catch erlang:delete_module(my_code_test2), catch erlang:purge_module(my_code_test2), ?line {ok,my_code_test2} = c:c(File, [{outdir,Priv} | Opts]), ?line IsNative = lists:member(native,Opts), ?line IsNative = code:is_module_native(my_code_test2), ?line T = ets:new(my_code_test2_fun_table, []), ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}), ets:insert(T, {my_fun2,my_code_test2:make_fun2()}), Papa = self(), {P0,M0} = spawn_monitor(fun () -> [{my_fun2,F2}] = ets:lookup(T, my_fun2), F2(fun () -> Papa ! {self(),"going to sleep"}, receive {Papa,"wake up"} -> ok end end, fun () -> ok end), exit(completed) end), ?line PurgeType = case Type of code_gone -> ok = file:delete(Code++".beam"), true; code_reload -> true; code_there -> false end, ?line true = erlang:delete_module(my_code_test2), ?line ok = receive {P0, "going to sleep"} -> ok after 1000 -> timeout end, ?line Purge = start_purge(my_code_test2, PurgeType), ?line {P1, M1} = spawn_monitor(fun () -> ?line [{my_fun,F}] = ets:lookup(T, my_fun), ?line 4712 = F(1), exit(completed) end), ?line ok = wait_until(fun () -> {status, suspended} == process_info(P1, status) end), ?line ok = continue_purge(Purge), ?line {P2, M2} = spawn_monitor(fun () -> ?line [{my_fun,F}] = ets:lookup(T, my_fun), ?line 4713 = F(2), exit(completed) end), ?line {P3, M3} = spawn_monitor(fun () -> ?line [{my_fun,F}] = ets:lookup(T, my_fun), ?line 4714 = F(3), exit(completed) end), ?line ok = wait_until(fun () -> {status, suspended} == process_info(P2, status) end), ?line ok = wait_until(fun () -> {status, suspended} == process_info(P3, status) end), ?line {current_function, {erts_code_purger, pending_purge_lambda, 3}} = process_info(P1, current_function), ?line {current_function, {erts_code_purger, pending_purge_lambda, 3}} = process_info(P2, current_function), ?line {current_function, {erts_code_purger, pending_purge_lambda, 3}} = process_info(P3, current_function), case Type of code_there -> ?line false = complete_purge(Purge), P0 ! {self(), "wake up"}, ?line completed = wait_for_down(P0,M0); _ -> ?line {true, true} = complete_purge(Purge), ?line killed = wait_for_down(P0,M0) end, case Type of code_gone -> ?line {undef, _} = wait_for_down(P1,M1), ?line {undef, _} = wait_for_down(P2,M2), ?line {undef, _} = wait_for_down(P3,M3); _ -> ?line completed = wait_for_down(P1,M1), ?line completed = wait_for_down(P2,M2), ?line completed = wait_for_down(P3,M3), catch erlang:purge_module(my_code_test2), catch erlang:delete_module(my_code_test2), catch erlang:purge_module(my_code_test2) end, ok. wait_for_down(P,M) -> receive {'DOWN', M, process, P, Reason} -> Reason after 1000 -> timeout end. wait_until(Fun) -> wait_until(Fun, 20). wait_until(Fun, N) -> case {Fun(),N} of {true, _} -> ok; {false, 0} -> timeout; {false, _} -> receive after 100 -> ok end, wait_until(Fun, N-1) end. start_purge(Mod, Type) when is_atom(Mod) andalso ((Type == true) orelse (Type == false)) -> Ref = make_ref(), erts_code_purger ! {test_purge, Mod, self(), Type, Ref}, receive {started, Ref} -> Ref end. continue_purge(Ref) when is_reference(Ref) -> erts_code_purger ! {continue, Ref}, receive {continued, Ref} -> ok end. complete_purge(Ref) when is_reference(Ref) -> erts_code_purger ! {complete, Ref}, receive {test_purge, Res, Ref} -> Res end.