Chinaxing
Chinaxing

Reputation: 8874

erlang race condition of spawn and receive

I am learning erlang with book , In chapter 13, the first exercise is writing a function my_spawn, which catch exit message when spawned message crash/exited.

-module(my_spawn1).
-compile(export_all).

my_spawn(Mod,Func,Args) ->
    {M1, S1, Mi1} = os:timestamp(),
    Pid = spawn(Mod,Func,Args),
    lib_misc:on_exit(Pid, fun(Why) ->
                  {M2,S2,Mi2} = os:timestamp(),
                  ElapsedTime = (M2 - M1) * 1000000 + (S2 - S1) * 1000 + (Mi2-Mi1),
                  io:format("~p died with:~p~n consume time:~p(ms)", [Pid,Why,ElapsedTime]),
              end),
    Pid.

my confuse was, if after spawn_monitor, the Mod:Func(Args) was finished, but the lib_misc:on_exit(...) haven't setup, so the exit message will be lost, really ?

If that's correct, so how to catch this situation ?

[edit by Pascal]

I add the code for lib_misc:on_exit/2

on_exit(Pid, Fun) ->
    spawn(fun() -> 
          process_flag(trap_exit, true), %% <label id="code.onexit1"/>
          link(Pid),                     %% <label id="code.onexit2"/>
          receive
              {'EXIT', Pid, Why} ->      %% <label id="code.onexit3"/>
              Fun(Why)   %% <label id="code.onexit4"/>
          end
      end).

Upvotes: 0

Views: 198

Answers (2)

Pascal
Pascal

Reputation: 14042

The first thing the function on_exit does is to spawn a process that set the process_flag trap_exit to true, thus it is "protected" from crashes, and will receive instead messages of the type: {'EXIT', Pid, Why}.

On the next line it tries to link itself to Pid; 2 cases are possible:

  • the Pid does not exists (wrong value, already dead...) then the on_exit process will receive the message {'EXIT', Pid, noproc} and will call F(noproc).
  • the Pid exists, then the on_exit process will wait on receive until the process Pid dies for some Reason. on_exit will receive {'EXIT', Pid, Reason} and call F(Reason).

I don't understand why you speak about spawn_monitor, it is not used in your case. Anyway, if you replace link(Pid) by monitor(process,Pid) in the on_exit function, you even don't need to use trap_exit since the monitor function don't crash if the Pid is dead. In all cases it returns the message {'DOWN',MonitorReference,process,Pid,Reason}. on_exit could be modified like this:

on_exit(Pid, Fun) ->
    spawn(fun() -> 
          MonitorReference = monitor(process,Pid),
          receive
              {'DOWN',MonitorReference,process,Pid,Why} -> Fun(Why)
          end
      end).

Upvotes: 1

zxq9
zxq9

Reputation: 13154

No, the message (like all messages) will be queued. If the process receiving the message dies, though, its mailbox is lost.

If A does spawn_monitor to spawn B, and B dies instantly, A is guaranteed to receive the DOWN message. If, however, A also dies, then everything in A's message queue is lost.

Upvotes: 1

Related Questions