[erlang-questions] Order of message processing by a gen_server
Andreas Hillqvist
andreas.hillqvist@REDACTED
Wed Dec 5 11:51:29 CET 2007
You could solve the problem using a priority queue in a separate process:
loop(?EMPTY) ->
receive
{push, NewPriority, NewMessage} = Msg ->
NewQueue = [{NewPriority, NewMessage}],
loop(NewQueue);
end;
loop([{Priority, _} | _] = Queue) ->
receive
{push, NewPriority, NewMessage} = Msg when NewPriority > Priority ->
NewQueue = [{NewPriority, NewMessage} | Queue],
loop(NewQueue);
{pull, From, Ref} = Msg ->
[{_, Message} | NewQueue] = Queue,
Reply = {Ref, Message},
From ! Reply,
loop(NewQueue);
end.
I have created and attached a example priority queue that applies the
OTP principles.
I remember that the question of priority queue has been up before.
Is this something that would be useful as a part of Erlang/OTP?
Maybe using some other principle.
Kind regards,
Andreas Hillqvist
2007/12/5, Andreas Hillqvist <andreas.hillqvist@REDACTED>:
> 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
> >
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: priority_queue.erl
Type: application/octet-stream
Size: 2587 bytes
Desc: not available
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20071205/e14cb5c7/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test_priority_queue.erl
Type: application/octet-stream
Size: 917 bytes
Desc: not available
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20071205/e14cb5c7/attachment-0001.obj>
More information about the erlang-questions
mailing list