uwiger / gproc

Extended process registry for Erlang
Apache License 2.0
1.07k stars 232 forks source link

awaitee pid exiting unregisters name of process it was awaiting #74

Closed mokele closed 9 years ago

mokele commented 10 years ago
(go@127.0.0.1)2> K = {n,g,myproc}.            
{n,g,myproc}
(go@127.0.0.1)3> gproc:where(K).
undefined
(go@127.0.0.1)4> spawn(fun() -> io:format("awaited: ~p~n", [gproc:await(K)]), exit(normal) end).
<0.190.0>
(go@127.0.0.1)5> ets:tab2list(gproc).
[{{<0.190.0>,g}},
 {{<0.190.0>,{n,g,myproc}},[]},
 {{{n,g,myproc},n},[{<0.190.0>,#Ref<0.0.0.412>}]}]
(go@127.0.0.1)6> spawn(fun() -> gproc:reg(K), receive stop -> ok end end).   
<0.193.0>
awaited: {<0.193.0>,undefined}
(go@127.0.0.1)7> gproc:where(K).
undefined
(go@127.0.0.1)8> ets:tab2list(gproc).                                     
[{{<0.193.0>,g}},{{<0.193.0>,{n,g,myproc}},[]}]

As you can see in the example above, the entry for {{n,g,myproc},n},<0.193.0>,undefined} has been deleted and the g and notify list entries are still sitting around by themselves and are no use to anyone since the {Key,n} entry has been deleted.

Here is the same example with some helpful debugging printouts:

(go@127.0.0.1)2> K = {n,g,myproc}.            
{n,g,myproc}
(go@127.0.0.1)3> gproc:where(K).
undefined
(go@127.0.0.1)4> spawn(fun() -> io:format("awaited: ~p~n", [gproc:await(K)]), exit(normal) end).
gproc_lib:ensure_monitor(<0.190.0>,g)
<0.190.0>
(go@127.0.0.1)5> ets:tab2list(gproc).
[{{<0.190.0>,g}},
 {{<0.190.0>,{n,g,myproc}},[]},
 {{{n,g,myproc},n},[{<0.190.0>,#Ref<0.0.0.412>}]}]
(go@127.0.0.1)6> spawn(fun() -> gproc:reg(K), receive stop -> ok end end).   
<0.193.0>
gproc_lib:insert_reg({n,g,myproc}, undefined, <0.193.0>, g, registered)
awaited: {<0.193.0>,undefined}
(go@127.0.0.1)7> gproc_lib:ensure_monitor(<0.193.0>,g)
handle_info 'DOWN' <0.190.0>
handle_leader_cast pid_is_DOWN <0.190.0>
Globals [{{n,g,myproc},<0.190.0>}]
handle_leader_cast tab2list [{{<0.190.0>,{n,g,myproc}},[]},
                             {{<0.193.0>,g}},
                             {{<0.193.0>,{n,g,myproc}},[]},
                             {{{n,g,myproc},n},<0.193.0>,undefined}]
Opts []
maybe_failover remove_entry {n,g,myproc} <0.190.0>
handle_leader_cast after tab2list [{{<0.193.0>,g}},
                                   {{<0.193.0>,{n,g,myproc}},[]}]

(go@127.0.0.1)7> gproc:where(K).
undefined
(go@127.0.0.1)8> ets:tab2list(gproc).                                     
[{{<0.193.0>,g}},{{<0.193.0>,{n,g,myproc}},[]}]
uwiger commented 10 years ago

It's a bug. I'm on it.

mokele commented 10 years ago

cheers Ulf!

mokele commented 10 years ago

works great, thanks Ulf. I'm assuming in the future a separate ets table of monitored awaiting pids would be more efficient than the lookup-on-every-remove that is necessary due to the ensure monitored code being reused between the two different use-cases "monitor awaiting processes", and "monitor processes we have registered", but I guess that's a design decision to treat all processes that deal with gproc the same way.

uwiger commented 10 years ago

Yes, I have it on my todo list to separate the accesses, not least to reduce the contention on the ets table, but also to try to minimize the work done in the server process.