"Write a function start(AnAtom, Fun) to register AnAtom as spawn(Fun). Make sure that your program works correctly in the case when two parallel processes simultaneously evaluate start/2. In this case you must guarantee that one of these processes succeeds and the other fails."The solution proposed by Ladislav Lenart and slightly modified by Alain O'Dea included try->catch mechanism attempts to call the register(AnAtom, ...) concurrently, inside the try clause. The hope is that the "second" call will fire an exception which is being caught later:
-module (start_register).
-export ([start/2]).
start(Atom, Fun) ->
Registrant = self(),
spawn(
fun() ->
try register(Atom, self()) of
true ->
Registrant ! true,
Fun()
catch
error:badarg ->
Registrant ! false
end
end),
receive
true -> true;
false -> erlang:error(badarg)
end.
Does this work? I tried to use it manually and it did. But I could not simulate all possible situations which could happen when a lot of processes will try to bump upon this line. And even if I could, there is no guarantee something else won't happen which was not tested. Well, that is what exception mechanism is for, but...
but why bother? We have the full power of Erlang specifically designed to take care of concurrency problems!
Instead of performing "suspicious" actions I would "eliminate" concurrency here at all, moving it to the Erlang process mailbox. By allowing only a single dedicated process to register atoms. It is registered itself as registrar and serves requests to register other funs. Basically, I am using the Erlang's response to concurrency: "GET IN LINE!", meaning that the mechanism of storing messages in the process mailbox is very well tuned:
-module(prob8101).
-export([start/2,init_registrar/0]).
init_registrar() ->
register(registrar,spawn(fun() -> loop() end)).
loop() ->
receive
{register,Atom,Fun,From} ->
case whereis(Atom) of
undefined ->
register(Atom,spawn(Fun)),
From!{registered_ok,Atom,self()},
loop();
_Otherwise ->
From!{already_registered,Atom,self()},
loop()
end
end.
start(AnAtom, Fun) ->
registrar ! {register, AnAtom, Fun, self()},
Rid = whereis(registrar),
receive
{Result,AnAtom,Rid} -> Result
end.
Here we have only one process calling register(...) and we won't have any troubles with concurrent requests for there are none possible.
Here is the sample session:~/Documents/erlang myacct$ erl
Erlang (BEAM) emulator version 5.5.4 [source]
[async-threads:0] [kernel-poll:false]
Eshell V5.5.4 (abort with ^G)
1> c(prob8101).
{ok,prob8101}
2> prob8101:init_registrar().
true
3> prob8101:start(bbb,fun() ->
receive {From,A} -> From!{reply,A} end end).
{bbb,'is undefined'}
registered_ok
4> prob8101:start(bbb,fun() ->
receive {From,A} -> From!{reply,A} end end).
{bbb,'is defined',<0.40.0>}
was_registered
5>