<br><div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">From: Ladislav Lenart <<a href="mailto:lenartlad@volny.cz">lenartlad@volny.cz</a>
><br><br>start(Atom, Fun) when is_atom(Atom), is_function(Fun, 0) -><br>        Sender = self(),<br>        Fun2 = fun() -><br>                case catch register(Atom, self()) of<br>                        true ->
<br>                                Sender ! {started, self()},<br>                                Fun();<br>                        _ -><br>                                Sender ! {already_running, self()}<br>                end
<br>        end,<br>        Pid = spawn(Fun2),<br>        receive<br>                {started, Pid} -><br>                        {ok, Pid};<br>                {already_running, Pid} -><br>                        already_running
<br>        end.</blockquote><div><br>Thanks Ladislav, this seems to be the correct solution to the problem. Thanks also to Chris Newcombe who patiently explained to me how this works. For the future readers of this list, I'll reproduce my understanding of it here.
<br><br>The key insight I was missing is that "register/2" does an atomic test-and-set, so it will only register a given atom once, regardless of how it is called (and how many processes try concurrently). My original solution had the obvious race condition between the "whereis/1" and "register/2" calls. Just getting rid of the call to "whereis/1" wouldn't fix the problem, since you would have already spawned the process by the time you realized someone else had beaten you to registering it. Ladislav's solution starts a process every time "start/2" is called and then has that function register itself. If it succeeds, it runs the original fun, so now the registered atom is linked to the original fun (as the problem requires).
<br><br>I assumed this meant that the "register/2" BIF was keeping some global shared state that was also accessible via "whereis/1". Chris explained that a better way to look at it, in Erlang, is that register/2 acts as a server for some state (in this case, the registered atoms) and is able to atomically update that state when called by clients. So you can think of it as having a serialized queue of requests and responding to them in order. I'm not sure if that is how it is actually implemented, but it makes sense (to me) to think of it that way.
<br><br>Any mistakes in the above explanation are definitely mine!<br><br>Thanks again,<br>Charles Gordon<br></div></div>