[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