Monday, August 6, 2007

Programming Erlang exercise problem: trouble in the concurrent universe?

Going through Programming Erlang, encountered this exercise problem in Section 8.11:

Write a function start(AnAtom, Fun) to register AnAtom as spawn(Fun). Make sure 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.
Here is my first solution, and it seems to compile and run.
-module(register_function).
-export([start/2]).

start(AnAtom, Fun) ->
case whereis(AnAtom) of
undefined -> register(AnAtom, spawn(Fun));
_ -> error
end.
Now, is there race condition in the above? I mean, consider Processes A and B both executing start/2:
  1. Process A calls whereis(AnAtom) which evaluates to 'undefined';
  2. Process B calls whereis(AnAtom) which evaluates to 'undefined';
  3. Process A calls register(AnAtom, spawn(Fun)), which succeeds and returns true;
  4. Process B calls register(AnAtom, spawn(Fun)), which fails as AnAtom has been registered
Superficially, we have satisfied the requirements of the problem: Process A succeeded and B failed. But the sinister problem is that spawn(...) has been called by both processes anyway. Thus we have two instances of Fun being executed, with one of them in an anonymous process that I, at least for now, don't know how to access.

Ironically, the problem seems to be because presumably register puts AnAtom in some "global" table. Exactly the kind of concurrency problem that Erlang tries to avoid.

How would this be solved? Here is an idea:
  1. Create a "register_function" server;
  2. start/2 sends a message to this server with AnAtom and Fun as contents
  3. presumably the server processes one message at a time and the code between matching a message and executing some expressions is atomic with respect to other messages to the same server
Puff. You'd think Erlang would come with built in functionality for this. Is this important?

No comments: