[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