[erlang-questions] Order of message processing by a gen_server
Andreas Hillqvist
andreas.hillqvist@REDACTED
Wed Dec 5 09:20:22 CET 2007
It looks like you want to modle a finitive state machine(fsm). You
could use gen_fsm for this:
-module(my_fsm).
-define(NAME, ?MODULE).
-behaviour(gen_fsm).
%% External exports
-export([start_link/0,
cancel/0,
phase1/0,
phase2/0,
phase3/0,
get_state/0]).
%% gen_fsm exports
-export([init/1,
idle/2,
idle/3,
phase1/2,
phase1/3,
phase2/2,
phase2/3,
phase3/2,
phase3/3,
handle_event/3,
handle_sync_event/4,
handle_info/3,
terminate/3,
code_change/4]).
-record(state, {}).
%% External functions
start_link() ->
Args = [],
Options = [],
{ok, Pid} = gen_fsm:start_link({local, ?NAME}, ?MODULE, Args, Options).
cancel() ->
gen_fsm:send_all_state_event(?NAME, cancel).
phase1() ->
gen_fsm:send_event(?NAME, phase1).
phase2() ->
gen_fsm:send_event(?NAME, phase2).
phase3() ->
gen_fsm:send_event(?NAME, phase3).
get_state() ->
gen_fsm:sync_send_all_state_event(?NAME, get_state).
%% gen_fsm callbacks
init([]) ->
{ok, idle, #state{}}.
idle(phase1, StateData) ->
io:format("We have recived phase1 in idle!\n"
"We now transits to phase1\n"),
{next_state, phase1, StateData};
idle(_, StateData) ->
{next_state, idle, StateData}.
idle(_, From, StateData) ->
Reply = ok,
{reply, Reply, idle, StateData}.
phase1(phase2, StateData) ->
io:format("We have recived phase2 in phase1!\n"
"We now transits to phase2\n"),
{next_state, phase2, StateData};
phase1(_, StateData) ->
{next_state, phase1, StateData}.
phase1(_, From, StateData) ->
Reply = ok,
{reply, Reply, phase1, StateData}.
phase2(phase3, StateData) ->
io:format("We have recived phase3 in phase2!\n"
"We now transits to phase3\n"),
{next_state, phase3, StateData};
phase2(_, StateData) ->
{next_state, phase2, StateData}.
phase2(_, From, StateData) ->
Reply = ok,
{reply, Reply, phase2, StateData}.
phase3(_, StateData) ->
{next_state, phase3, StateData}.
phase3(_, From, StateData) ->
Reply = ok,
{reply, Reply, phase3, StateData}.
handle_event(cancel, StateName, StateData) ->
io:format("We have recived cancel in ~p!\n"
"We now transits to idle\n", [StateName]),
{next_state, idle, StateData}.
handle_sync_event(get_state, _From, StateName, StateData) ->
Reply = StateName,
{reply, Reply, StateName, StateData};
handle_sync_event(Event, From, StateName, StateData) ->
Reply = ok,
{reply, Reply, StateName, StateData}.
handle_info(Info, StateName, StateData) ->
{next_state, StateName, StateData}.
terminate(Reason, StateName, StatData) ->
ok.
code_change(OldVsn, StateName, StateData, Extra) ->
{ok, StateName, StateData}.
Here is a test suit that illustrates when it transits:
-module(test_my_fsm).
-export([start/0]).
start() ->
{ok, Pid} = my_fsm:start_link(),
idle = my_fsm:get_state(),
my_fsm:cancel(),
idle = my_fsm:get_state(),
my_fsm:phase1(),
phase1 = my_fsm:get_state(),
my_fsm:cancel(),
idle = my_fsm:get_state(),
my_fsm:phase2(),
idle = my_fsm:get_state(),
my_fsm:phase1(),
my_fsm:phase2(),
phase2 = my_fsm:get_state(),
my_fsm:cancel(),
idle = my_fsm:get_state(),
my_fsm:phase3(),
idle = my_fsm:get_state(),
my_fsm:phase1(),
my_fsm:phase3(),
phase1 = my_fsm:get_state(),
my_fsm:phase2(),
my_fsm:phase3(),
phase3 = my_fsm:get_state(),
my_fsm:cancel(),
idle = my_fsm:get_state(),
success.
There may problem, if the processing of state take some time.
Example:
1. phase1 is sent to my_fms
2. my_fms receives phase1
3. my_fms starts processing phase1
4. phase2 is sent to my_fms
5. phase3 is sent to my_fms
6. cancel is sent to my_fms
7. my_fms finish processing phase1
8. my_fms receives phase2
9. my_fms starts processing phase2
10. my_fms finish processing phase2
11. my_fms receives phase3
12. my_fms starts processing phase3
13. my_fms finish processing phase3
14. my_fms receives cancel
As I intrepid it, you want step 8 to receive cancel event and ignore
the transitions to phase2 and phase3?
If this is the case your loop would have to look like:
loop(State) ->
receive
cancel -> ...
after 0
receive
phase1 -> ...
phase2 -> ...
phase3 -> ...
cancel -> ...
end
end.
If you receive an cancel you should also empty the message box.
It may also be useful to use a reference to unice identify if the
transitions are valid or old.
Kind regards
Andreas Hillqvist
2007/12/5, Jack Orenstein <jao@REDACTED>:
> On Dec 5, 2007, at 12:40 AM, Lev Walkin wrote:
>
> > Jack Orenstein wrote:
> >> Suppose I have a gen_server process whose queue contains {a} and
> >> {b}. Does the order of the handle_call function clauses determine
> >> which message is processed first? I.e., if the function clauses are
> >> handle_call({a}, From, State) -> ...
> >> handle_call({b}, From, State) -> ...
> >> is it guaranteed that {a} will be processed first?
> >
> > Yes, but this is implementation detail and is not guaranteed
> > by the standard.
>
> What I am trying to do is to write a server that will process a
> series of requests, always the same requests (phase1, phase2, phase3)
> in the same order. But I want to be able to cancel the sequence of
> requests at any point, resetting the process state to be ready for
> phase1 again. If I write my own function with a receive, I can do this:
>
> loop(State) ->
> receive
> cancel -> ...
> phase1 -> ...
> phase2 -> ...
> phase3 -> ...
> end.
>
> Normally this server will receive messages phase1, phase2, phase3 in
> order. But if a cancel message shows up, e.g. after phase2, it takes
> priority (if I understand receive correctly).
>
> I'd like to use a gen_server, but it sounds like I can't do anything
> that is guaranteed by the language (not the implementation) to handle
> the cancel message with higher priority than any of the other
> messages. I.e.,
>
> handle_call({cancel}, From, State) -> ...
> handle_call({phase1}, From, State) -> ...
> handle_call({phase2}, From, State) -> ...
> handle_call({phase3}, From, State) -> ...
>
> isn't guaranteed to work similarly to the loop function above.
>
> Any advice on how to proceed?
>
> Jack Orenstein
>
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://www.erlang.org/mailman/listinfo/erlang-questions
>
More information about the erlang-questions
mailing list