diff --git a/src/spr_util.erl b/src/spr_util.erl index a4bbde0..bec8a97 100644 --- a/src/spr_util.erl +++ b/src/spr_util.erl @@ -113,7 +113,12 @@ do_f(Parent, Ref, F, I) -> gather([Pid|T]=L, Ref, Timeout, Acc) -> receive {Pid, Ref, Ret} -> gather(T, Ref, Timeout, [Ret|Acc]); - {'EXIT', _, Reason} -> + % NOTE: This can only originate from non-worker processes, as they + % are spawned and not linked. There is also a catch for workers, + % so they just return {'EXIT', Reason} when they fail instead of sending a message. + {'EXIT', _SourcePID, normal} -> + gather(L, Ref, Timeout, Acc); + {'EXIT', _SourcePID, Reason} -> _ = [exit(P, Reason) || P <- L], wait_until_dead(L), exit(Reason) diff --git a/test/spr_util_eunit.erl b/test/spr_util_eunit.erl index 04baa1c..6c878a6 100644 --- a/test/spr_util_eunit.erl +++ b/test/spr_util_eunit.erl @@ -75,6 +75,21 @@ pmap_test_() -> PossibleRemains = PidsAfter -- PidsBefore, ?assertMatch([], [P || P <- PossibleRemains, is_process_alive(P)]) end), + % Ensure that when a semi-related process exits cleanly we do not kill the workers + ?_test(begin + Input = lists:seq(1, 5), + Worker = fun (Value) -> timer:sleep(1000), Value end, + erlang:spawn_link(fun() -> timer:sleep(100), exit(self(), normal) end), + Results = spr_util:pmap(Worker, Input), + ?assertEqual(Input, Results) + end), + % Check that an error in the worker causes the error to be returned + ?_test(begin + Input = lists:seq(1, 5), + Worker = fun (1) -> exit(normal); (Value) -> Value end, + Results = spr_util:pmap(Worker, Input), + ?assertEqual([{'EXIT', normal}, 2, 3, 4, 5], Results) + end), %% The trap_exti flag for the caller is kept as it was ?_test(begin Self=self(),