-module(call_purged_fun_tester).
-export([do/4]).
do(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),
{ok,my_code_test2} = c:c(File, [{outdir,Priv} | Opts]),
IsNative = lists:member(native,Opts),
IsNative = code:is_module_native(my_code_test2),
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()}),
spawn(fun () ->
[{my_fun2,F2}] = ets:lookup(T, my_fun2),
F2(fun () ->
receive after infinity -> ok end
end,
fun () -> ok end),
exit(completed)
end),
PurgeType = case Type of
code_gone ->
ok = file:delete(Code++".beam"),
true;
code_reload ->
true;
code_there ->
false
end,
true = erlang:delete_module(my_code_test2),
Purge = start_purge(my_code_test2, PurgeType),
{P0, M0} = spawn_monitor(fun () ->
[{my_fun,F}] = ets:lookup(T, my_fun),
4712 = F(1),
exit(completed)
end),
wait_until(fun () ->
{status, suspended}
== process_info(P0, status)
end),
ok = continue_purge(Purge),
{P1, M1} = spawn_monitor(fun () ->
[{my_fun,F}] = ets:lookup(T, my_fun),
4713 = F(2),
exit(completed)
end),
{P2, M2} = spawn_monitor(fun () ->
[{my_fun,F}] = ets:lookup(T, my_fun),
4714 = F(3),
exit(completed)
end),
wait_until(fun () ->
{status, suspended}
== process_info(P1, status)
end),
wait_until(fun () ->
{status, suspended}
== process_info(P2, status)
end),
{current_function,
{erts_code_purger,
pending_purge_lambda,
3}} = process_info(P0, current_function),
{current_function,
{erts_code_purger,
pending_purge_lambda,
3}} = process_info(P1, current_function),
{current_function,
{erts_code_purger,
pending_purge_lambda,
3}} = process_info(P2, current_function),
case Type of
code_there ->
false = complete_purge(Purge);
_ ->
{true, true} = complete_purge(Purge)
end,
case Type of
code_gone ->
receive
{'DOWN', M0, process, P0, Reason0} ->
{undef, _} = Reason0
end,
receive
{'DOWN', M1, process, P1, Reason1} ->
{undef, _} = Reason1
end,
receive
{'DOWN', M2, process, P2, Reason2} ->
{undef, _} = Reason2
end;
_ ->
receive
{'DOWN', M0, process, P0, Reason0} ->
completed = Reason0
end,
receive
{'DOWN', M1, process, P1, Reason1} ->
completed = Reason1
end,
receive
{'DOWN', M2, process, P2, Reason2} ->
completed = Reason2
end,
catch erlang:purge_module(my_code_test2),
catch erlang:delete_module(my_code_test2),
catch erlang:purge_module(my_code_test2)
end,
ok.
wait_until(Fun) ->
ok = 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.