Generic server and selective receives

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Generic server and selective receives

Ludovic Demblans
Hi,

I would like some help to implement a message-buffer in erlang. This
process can be sent messages, and stores them in mailbox. A buffer can
have one client, or can have no client. When there's no client, I
would prevent the process to do anything but accept a new client or
timeout after a small amount of time. So I have two sets of receive
clauses.

Now, I would like to implement this as a gen_server. But I don't know
how. The Idea is to timeout quickly if no message of the expected
clauses-set comes.

* I thought of a gen_fsm but it will crash if it's sent a message when
in the "no client" state, right ? (Plus i may want to handle more
receive clauses in the "no client" part)
* I thougt about time calculations but it's seems too complicated for
no reason ; I prefer use the simple gen_* timeouts.
* I could keep my current implementation and adapt the module to fit
in a supervision tree but I believe I willll miss common OTP features.

So, I hope this is a common pattern and that someone will tell me a
good solution.

Thank you.

-- lud

This is my current implementation (but not the actual code).

     loop(#state{client_pid=undefined}) ->
         receive
             {set_client,Pid} ->
                 loop(State#state{client_pid=Pid})
         after 10000 ->
             io:fwrite("Terminating\n")
         end;

     loop(State=#state{client_pid=Client,mailbox=Mailbox}) ->
         receive
             {set_client,Pid} ->
                 %% here we can change the client or set it to undefined
                 loop(State#state{client_pid=Pid});
             {message,M} ->
                 Client ! you_have_messages,
                 loop(State#state{mailbox=[M|Mailbox]});
             %% A handful of other clauses
             ...
                 ...
                 ...
         after way more time than 1000 ->
             io:fwrite("Terminating too\n")
         end;


Now i'll pray for this mail to be seen in the current huge amount of
emails (and very intersting by the way) on the list.


Reply | Threaded
Open this post in threaded view
|

Generic server and selective receives

Vance Shipley-2
On Mon, Feb 17, 2014 at 12:55:19AM +0100, ludovic wrote:
}  Now, I would like to implement this as a gen_server. But I don't know
}  how. The Idea is to timeout quickly if no message of the expected
}  clauses-set comes.
}  
}  * I thought of a gen_fsm but it will crash if it's sent a message when
}  in the "no client" state, right ? (Plus i may want to handle more
}  receive clauses in the "no client" part)

In the example below it will crash if anything but a '{set_client, Pid}'
event is received during the 'wait_for_client' state but until you define
what it should do that is fine.  If it should handle additional events
add clauses to wait_for_client/2.

}  * I thougt about time calculations but it's seems too complicated for
}  no reason ; I prefer use the simple gen_* timeouts.

It's not.

}  * I could keep my current implementation and adapt the module to fit
}  in a supervision tree but I believe I willll miss common OTP features.

Unless you reimplement most of gen.erl you certainly will.


-module(mbox_fsm).

-behaviour(gen_fsm).

-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
         terminate/3, code_change/4]).
-export([wait_for_client/2, wait_for_request/2]).

-define(MAXTIME, 60000).

-record(statedata, {client_pid, mailbox = [], timeout}).

init(_Arg) ->
        process_flag(trap_exit, true),
        {ok, wait_for_client, #statedata{}, 10000}.

wait_for_client({set_client, Pid}, StateData) ->
        NextStateData = StateData#statedata{client_pid = Pid},
        {next_state, wait_for_request, NextStateData, ?MAXTIME}.

wait_for_request({set_client, Pid},
                        #statedata{timeout = Then} = StateData) ->
        NextTimeout = ?MAXTIME - timer:now_diff(os:timestamp(), Then),
        NextStateData = StateData#statedata{client_pid = Pid,
                        timeout = NextTimeout},
        {next_state, wait_for_request, NextStateData, NextTimeout};
wait_for_request({message, M},
                StateData#statedata{mailbox = Mailbox, timeout = Then}) ->
        NextTimeout = ?MAXTIME - timer:now_diff(os:timestamp(), Then),
        NextStateData = StateData#statedata{{mailbox = [M | Mailbox]},
        {next_state, wait_for_request, NextStateData, NextTimeout};
% A handful of other clauses
wait_for_request(timeout, StateData) ->
        {stop, timeout, StateData}.

handle_info(Info, _StateName, StateData) ->
        {stop, Info, StateData}.

terminate(_Reason, _StateName, _StateData) ->
        ok.

code_change(_OldVsn, StateName, StateData, _Extra) ->
        {ok, StateName, StateData}.

--
        -Vance

Reply | Threaded
Open this post in threaded view
|

Generic server and selective receives

Siraaj Khandkar
In reply to this post by Ludovic Demblans
With gen_server, you have no choice but to handle a message. Once you
have it, you can defer processing by stashing it in your process' state.
If you want to reject messages when client_pid=undefined, then you may
want to consider a protocol with receipt acknowledgements, notifying the
sender if the message was accepted or discarded.


On 2/16/14, 6:55 PM, ludovic wrote:

> Hi,
>
> I would like some help to implement a message-buffer in erlang. This
> process can be sent messages, and stores them in mailbox. A buffer can
> have one client, or can have no client. When there's no client, I
> would prevent the process to do anything but accept a new client or
> timeout after a small amount of time. So I have two sets of receive
> clauses.
>
> Now, I would like to implement this as a gen_server. But I don't know
> how. The Idea is to timeout quickly if no message of the expected
> clauses-set comes.
>
> * I thought of a gen_fsm but it will crash if it's sent a message when
> in the "no client" state, right ? (Plus i may want to handle more
> receive clauses in the "no client" part)
> * I thougt about time calculations but it's seems too complicated for
> no reason ; I prefer use the simple gen_* timeouts.
> * I could keep my current implementation and adapt the module to fit
> in a supervision tree but I believe I willll miss common OTP features.
>
> So, I hope this is a common pattern and that someone will tell me a
> good solution.
>
> Thank you.
>
> -- lud
>
> This is my current implementation (but not the actual code).
>
>     loop(#state{client_pid=undefined}) ->
>         receive
>             {set_client,Pid} ->
>                 loop(State#state{client_pid=Pid})
>         after 10000 ->
>             io:fwrite("Terminating\n")
>         end;
>
>     loop(State=#state{client_pid=Client,mailbox=Mailbox}) ->
>         receive
>             {set_client,Pid} ->
>                 %% here we can change the client or set it to undefined
>                 loop(State#state{client_pid=Pid});
>             {message,M} ->
>                 Client ! you_have_messages,
>                 loop(State#state{mailbox=[M|Mailbox]});
>             %% A handful of other clauses
>             ...
>                 ...
>                 ...
>         after way more time than 1000 ->
>             io:fwrite("Terminating too\n")
>         end;
>
>
> Now i'll pray for this mail to be seen in the current huge amount of
> emails (and very intersting by the way) on the list.

Reply | Threaded
Open this post in threaded view
|

Generic server and selective receives

Anthony Ramine-2
In reply to this post by Vance Shipley-2
You don?t have to reimplement ?gen? to implement a sys process, no.

--
Anthony Ramine

Le 17 f?vr. 2014 ? 06:15, Vance Shipley <vances> a ?crit :

> Unless you reimplement most of gen.erl you certainly will.


Reply | Threaded
Open this post in threaded view
|

Generic server and selective receives

Daniel Goertzen-4
In reply to this post by Ludovic Demblans
Take a look at plain_fsm:

https://github.com/uwiger/plain_fsm/blob/master/doc/plain_fsm.md

Dan.


On Sun, Feb 16, 2014 at 5:55 PM, <ludovic> wrote:

> Hi,
>
> I would like some help to implement a message-buffer in erlang. This
> process can be sent messages, and stores them in mailbox. A buffer can
> have one client, or can have no client. When there's no client, I
> would prevent the process to do anything but accept a new client or
> timeout after a small amount of time. So I have two sets of receive
> clauses.
>
> Now, I would like to implement this as a gen_server. But I don't know
> how. The Idea is to timeout quickly if no message of the expected
> clauses-set comes.
>
> * I thought of a gen_fsm but it will crash if it's sent a message when
> in the "no client" state, right ? (Plus i may want to handle more
> receive clauses in the "no client" part)
> * I thougt about time calculations but it's seems too complicated for
> no reason ; I prefer use the simple gen_* timeouts.
> * I could keep my current implementation and adapt the module to fit
> in a supervision tree but I believe I willll miss common OTP features.
>
> So, I hope this is a common pattern and that someone will tell me a
> good solution.
>
> Thank you.
>
> -- lud
>
> This is my current implementation (but not the actual code).
>
>     loop(#state{client_pid=undefined}) ->
>         receive
>             {set_client,Pid} ->
>                 loop(State#state{client_pid=Pid})
>         after 10000 ->
>             io:fwrite("Terminating\n")
>         end;
>
>     loop(State=#state{client_pid=Client,mailbox=Mailbox}) ->
>         receive
>             {set_client,Pid} ->
>                 %% here we can change the client or set it to undefined
>                 loop(State#state{client_pid=Pid});
>             {message,M} ->
>                 Client ! you_have_messages,
>                 loop(State#state{mailbox=[M|Mailbox]});
>             %% A handful of other clauses
>             ...
>                 ...
>                 ...
>         after way more time than 1000 ->
>             io:fwrite("Terminating too\n")
>         end;
>
>
> Now i'll pray for this mail to be seen in the current huge amount of
> emails (and very intersting by the way) on the list.
>
> _______________________________________________
> erlang-questions mailing list
> erlang-questions
> http://erlang.org/mailman/listinfo/erlang-questions
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20140217/82368569/attachment.html>

Reply | Threaded
Open this post in threaded view
|

Generic server and selective receives

Ludovic Demblans
That's very interesting.

Many thanks.

Cheers,

Ludovic

Le 2014-02-17 15:07, Daniel Goertzen a ?crit?:

> Take a look at plain_fsm:
>
> https://github.com/uwiger/plain_fsm/blob/master/doc/plain_fsm.md [2]
>
> Dan.
>
> On Sun, Feb 16, 2014 at 5:55 PM, <ludovic> wrote:
>
>
> Links:
> ------
> [1] http://erlang.org/mailman/listinfo/erlang-questions
> [2] https://github.com/uwiger/plain_fsm/blob/master/doc/plain_fsm.md