Transaction Mnesia will do just that thing for you. This is a transaction if you are not performing dirty operations. So just put your write and read operations in one transaction, and mnesia will take a break. All operations in one transaction are performed as one atomic operation. Mnesia's transaction isolation level is what is sometimes called "serializable," i.e. The strongest level of isolation.
Edit:
You seem to have missed one important point about parallel processes in Erlang. (To be fair, this is not only true in Erlang, but also in any truly parallel environment, and when someone argues differently, this is not a true parallel environment.) You cannot distinguish which action occurs earlier and what the second, if You do not do some synchronization. Only in this way can you perform this synchronization using messaging. You have guaranteed only one message message in Erlang, the ordering of messages sent from one process to another process. This means that when you send two messages M1 and M2 from process A to process B they arrive in the same order. But if you send message M1 from A to B and message M2 from C to B , they can arrive in any order. Just because you can even say which message you sent first? Even worse, if you send a message M1 from A to B and then M2 from A to C and when M2 arrives at C sends M3 from C to B you did not guarantee that M1 will reach B to M3 . Even this will happen in one virtual machine in the current implementation. But you cannot rely on it, because it is not guaranteed and can change even in the next version of VM, only thanks to the implementation of message passing between different schedulers.
It illustrates the problems of ordering events in parallel processes. Now back to the mnesia transaction. Mnesia surgery should be free from the side effects of fun . This means that there can be no messages sent outside the transaction. Thus, you cannot determine which transaction starts from the beginning and at the start. The only thing you can say if the transaction is successful, and they order, you can only determine by its effect. When you consider this, your subtle explanation does not make sense. One transaction will read all the keys in the atomic operation, even if it is implemented as reading one key by one in the implementation of the transaction, and your write operation will also be performed as an atom operation. You cannot determine if the fourth key was recorded in the second transaction after you read the 1st key in the first transaction, because there it is not observed externally. Both transactions will be executed in a specific order as a separate atomic operation. From an external point of view, all keys will be read at the same moment in time, and it is mnesia's job to force it. If you send a message from within a transaction, you are violating the mnesia transaction property, and you cannot be surprised that it will behave strangely. To be specific, this message can be sent many times.
Edit2:
If you deploy even a few of these processes, you will see that a, b and c are very quickly in a state where their values do not add up to 10.
I'm curious why you think this will happen, or did you test it? Show me your test case and I will show my:
-module(transactions). -export([start/2, sum/0, write/0]). start(W, R) -> mnesia:start(), {atomic, ok} = mnesia:create_table(test, [{ram_copies,[node()]}]), F = fun() -> ok = mnesia:write({test, a, 10}), [ ok = mnesia:write({test, X, 0}) || X <- [b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]], ok end, {atomic, ok} = mnesia:transaction(F), F2 = fun() -> S = self(), erlang:send_after(1000, S, show), [ spawn_link(fun() -> writer(S) end) || _ <- lists:seq(1,W) ], [ spawn_link(fun() -> reader(S) end) || _ <- lists:seq(1,R) ], collect(0,0) end, spawn(F2). collect(R, W) -> receive read -> collect(R+1, W); write -> collect(R, W+1); show -> erlang:send_after(1000, self(), show), io:format("R: ~p, W: ~p~n", [R,W]), collect(R, W) end. keys() -> element(random:uniform(6), {[a,b,c],[a,c,b],[b,a,c],[b,c,a],[c,a,b],[c,b,a]}). sum() -> F = fun() -> lists:sum([X || K<-keys(), {test, _, X} <- mnesia:read(test, K)]) end, {atomic, S} = mnesia:transaction(F), S. write() -> F = fun() -> [A, B ] = L = [ random:uniform(10) || _ <- [1,2] ], [ok = mnesia:write({test, K, V}) || {K, V} <- lists:zip(keys(), [10-AB|L])], ok end, {atomic, ok} = mnesia:transaction(F), ok. reader(P) -> case sum() of 10 -> P ! read, reader(P); _ -> io:format("ERROR!!!~n",[]), exit(error) end. writer(P) -> ok = write(), P ! write, writer(P).
If this does not work, it will be a really serious problem. There are serious applications, including payment systems, that rely on it. If you have a test case that shows that it is broken, report an error on the erlang-bugs@erlang.org. page