[erlang-questions] example of race conditions ?

David King dking@REDACTED
Tue Jul 31 18:48:27 CEST 2007


> Can somebody share sample code that *has* race conditions ?
> Appreciate a response from the experienced erlangers.

I'm not that experienced, but here goes:

-module(foo).
-export([get/0,set/1]). % external callers
-export([daemon_loop/1]). % for spawn()
-define(initial_value,0).
daemon_loop(CurrentValue) ->
	receive
		{get,Callback} ->
			CallBack ! {value,CurrentValue},
			daemon_loop(CurrentValue);
		{set,Value}
			daemon_loop(Value);
		die -> ok;
	end.
get() ->
	ensure_running(),
	daemon ! {get,self()},
	receive {value,Value} ->
		Value
	end.
set(Value)
	ensure_running(),
	daemon ! {set,Value}.
ensure_running() ->
	case erlang:whereis(daemon) of
		undefined -> start_daemon();
		Pid -> ok
	end.
start_daemon() ->
	DaemonPid=spawn(?MODULE,daemon_loop,[?initial_value]),
	register(daemon_loop,DaemonPid).

So that's a simple program that allows you to set some global  
variable by spawning a daemon to maintain its value and communicate  
it, like this:

foo:set(cheese),
true=(cheese==foo:get()).

You can call these from separate processes and get/set their values  
correctly. Since only one process enters the daemon_loop at a time  
(because messages to it are queued until retrieved), gets and sets of  
"Value" are thread-safe.

However, if I call get/0 and set/1 at the same time, ensure_running/0  
will be called twice, at the same time. The first instance of  
"whereis" will return undefined, and the second instance of "whereis"  
may also return undefined if the first process is pre-empted (or  
running on another CPU at the same time) in between the "whereis" and  
the "register". What can happen is that you can get two daemon_loops  
running, one which becomes unreachable, which could be the initial  
value that you set.

 From the perspective of a user of this module, you could get a case  
where running
	foo:set(1),
	true=(1==foo:get()).
and
	TheValue=foo:get().

at the same time can cause the first process to die with a badmatch.

Notice how the thread-unsafe part of this is the global data that is  
shared: the table of registered name/PIDs. The "Value" is not shared,  
and its accesses are thread-safe. Does that all make sense?



More information about the erlang-questions mailing list