[erlang-questions] Deterministic playback of a simulation?
Richard Evans
richardprideauxevans@REDACTED
Fri May 18 04:36:05 CEST 2012
I have a multiplayer simulation game running in erlang, involving
multiple characters making decisions etc. Each game instance uses a
few (currently, 4) erlang processes running concurrently,
communicating with an AI simulator written in C.
Everything is working well and I am enjoying using erlang.
But there is one thing I am finding tricky.
For QA purposes, I want to be able to record the exact sequence of
function-calls, so I can play the game back exactly,
deterministically.
My current approach is this: I have a global ets table storing a list
of function-calls. Then, whenever I was calling a function I want to
record - instead of calling the function directly - I call a procedure
which adds the function to the list of function-calls, and then calls
it:
call_and_store(Fun, Args) ->
add_to_script(Fun, Args);
apply(lobby, Fun, Args).
add_to_script(Fun, Args) ->
Info = ets:info(script),
{size, Size} = lists:keyfind(size, 1, Info),
io:format("Inserting {~p,~p,~p}~n", [Size,Fun,Args]),
ets:insert(script, {Size, Fun, Args}).
This approach does not seem to work well because of erlang's
pre-emptive scheduling. If two concurrent processes both invoke
call_and_store, then it is possible (and seems to actually be
happening) that erlang's pre-emptive scheduler may stop processing one
instance of call_and_store between execution of add_to_script and
execution of apply. If this happens, the order of execution and the
order of recorded function-calls will diverge, we don't have an
accurate recording of the set of function-calls, and we won't be able
to deterministically playback.
So my question is: is there a way to prevent the scheduler from
yielding during a block of code? Some way to insist the block is
called as one unit, like this:
call_and_store(Fun, Args) ->
!!!prevent_yielding,
add_to_script(Fun, Args);
apply(lobby, Fun, Args),
!!!allow_yielding.
My guess is: no. This does not seem very erlangy.
Alternatively, is there a different approach which can produce the
desired list of function-calls? My current approach seems to go
against the erlang grain, involving writing to a shared global table.
Is there a better, more erlangy, way of doing this?
thanks,
~Richard
More information about the erlang-questions
mailing list