%%% -*- erlang-indent-level: 2 -*-
%%%-------------------------------------------------------------------
%%% Author: Kostis Sagonas
%%%
%%% Contains code examples that test correct handling of receives.
%%%-------------------------------------------------------------------
-module(basic_receive).
-export([test/0]).
test() ->
ok = test_wait_timeout(),
ok = test_double_timeout(),
ok = test_reschedule(),
ok = test_recv_mark(),
ok.
%%--------------------------------------------------------------------
test_wait_timeout() ->
receive after 42 -> ok end.
%%--------------------------------------------------------------------
test_double_timeout() ->
self() ! foo,
self() ! another_foo,
receive
non_existent -> weird
after 0 -> timeout
end,
receive
foo -> ok
after 1000 -> timeout
end.
%%--------------------------------------------------------------------
%% Check that RESCHEDULE returns from BIFs work.
test_reschedule() ->
erts_debug:set_internal_state(available_internal_state, true),
First = self(),
Second = spawn(fun() -> doit(First) end),
receive
Second -> ok
end,
receive
after 42 -> ok
end,
erts_debug:set_internal_state(hipe_test_reschedule_resume, Second),
ok.
doit(First) ->
First ! self(),
erts_debug:set_internal_state(hipe_test_reschedule_suspend, 1).
%%--------------------------------------------------------------------
%% Check that we cannot cause a recv_mark,recv_set pair to misbehave and
%% deadlock the process.
test_recv_mark() ->
ok = test_recv_mark(fun disturber_nop/0),
ok = test_recv_mark(fun disturber_receive/0),
ok = test_recv_mark(fun disturber_other/0),
ok = test_recv_mark(fun disturber_recurse/0),
ok = test_recv_mark_after(self(), fun disturber_after_recurse/0, false),
ok = test_recv_mark(fun disturber_other_recurse/0),
ok = test_recv_mark(fun disturber_other_after/0),
ok = test_recv_mark_nested().
test_recv_mark(Disturber) ->
Ref = make_ref(),
self() ! Ref,
Disturber(),
receive Ref -> ok
after 0 -> error(failure)
end.
disturber_nop() -> ok.
disturber_receive() ->
self() ! message,
receive message -> ok end.
disturber_other() ->
Ref = make_ref(),
self() ! Ref,
receive Ref -> ok end.
disturber_recurse() ->
aborted = (catch test_recv_mark(fun() -> throw(aborted) end)),
ok.
test_recv_mark_after(Recipient, Disturber, IsInner) ->
Ref = make_ref(),
Recipient ! Ref,
Disturber(),
receive
Ref -> ok
after 0 ->
case IsInner of
true -> expected;
false -> error(failure)
end
end.
disturber_after_recurse() ->
NoOp = fun() -> ok end,
BlackHole = spawn(NoOp),
expected = test_recv_mark_after(BlackHole, NoOp, true),
ok.
disturber_other_recurse() ->
aborted = (catch disturber_other_recurse(fun() -> throw(aborted) end)),
ok.
disturber_other_recurse(InnerD) ->
Ref = make_ref(),
self() ! Ref,
InnerD(),
receive Ref -> ok
after 0 -> error(failure)
end.
disturber_other_after() ->
BlackHole = spawn(fun() -> ok end),
Ref = make_ref(),
BlackHole ! Ref,
receive Ref -> error(imposible)
after 0 -> ok
end.
test_recv_mark_nested() ->
Ref1 = make_ref(),
self() ! Ref1,
begin
Ref2 = make_ref(),
self() ! Ref2,
receive Ref2 -> ok end
end,
receive Ref1 -> ok
after 0 -> error(failure)
end.
%%--------------------------------------------------------------------