Exceptions and gen_server

View: New views
10 Messages — Rating Filter:   Alert me  

Exceptions and gen_server

by Anton Krasovsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

What would be the standard way of returning/throwing an exception in a
result to a gen_server:call?

Would something like that be reasonable:

a() ->
case gen_server:call(..., a) of
    {ok, Value} -> Value;
    {error, Error} -> throw({error, Error})
end

and

handle_call(a, ...) ->
case ... of
    {ok, Value} -> {reply, {ok, Value} , State};
    {error, Error} -> {reply, {error, Error} , State}
end

Regards,
Anton

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Roberto Aloi-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Anton,

I usually do the following:

foo(Params) ->
    case gen_server:call(?MODULE, {foo, Params}) of
        {error, Reason} ->
            erlang:error({Reason, Params}); % I return params to
understand what's going on...
        {ok, Value} ->
            Value
    end.

handle_call({create, Name, Opts}, _From, State) ->
 ...

Regards,

Roberto Aloi
Erlang Training and Consulting Ltd.
http://www.erlang-consulting.com

Anton Krasovsky wrote:

> What would be the standard way of returning/throwing an exception in a
> result to a gen_server:call?
>
> Would something like that be reasonable:
>
> a() ->
> case gen_server:call(..., a) of
>     {ok, Value} -> Value;
>     {error, Error} -> throw({error, Error})
> end
>
> and
>
> handle_call(a, ...) ->
> case ... of
>     {ok, Value} -> {reply, {ok, Value} , State};
>     {error, Error} -> {reply, {error, Error} , State}
> end
>
> Regards,
> Anton
>
> ________________________________________________________________
> erlang-questions mailing list. See http://www.erlang.org/faq.html
> erlang-questions (at) erlang.org
>
>  


________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


RE: Exceptions and gen_server

by David Mercer-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Is there any particular reason you want to throw the value rather than just
erroring out?  If you need to throw it to a catch, I'd probably do as you
say, but otherwise, I'd just do a match and let an error happen when it
doesn't match.  Something like:

a() ->
        {ok, Value} = gen_server:call(..., a),
        Value.

Your handle_call looks OK to me.

Cheers,

David

> -----Original Message-----
> From: erlang-questions@... [mailto:erlang-questions@...] On
> Behalf Of Anton Krasovsky
> Sent: Friday, November 06, 2009 9:57 AM
> To: erlang-questions@...
> Subject: [erlang-questions] Exceptions and gen_server
>
> What would be the standard way of returning/throwing an exception in a
> result to a gen_server:call?
>
> Would something like that be reasonable:
>
> a() ->
> case gen_server:call(..., a) of
>     {ok, Value} -> Value;
>     {error, Error} -> throw({error, Error})
> end
>
> and
>
> handle_call(a, ...) ->
> case ... of
>     {ok, Value} -> {reply, {ok, Value} , State};
>     {error, Error} -> {reply, {error, Error} , State}
> end
>
> Regards,
> Anton
>
> ________________________________________________________________
> erlang-questions mailing list. See http://www.erlang.org/faq.html
> erlang-questions (at) erlang.org


________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Vance Shipley :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some time ago I was asking myself whether it was better to
throw an exception in the client or the server.  In the end
I decided your method was better because SASL handled it better.

        -Vance

--------------------------------------------------------------------------
-module(foo_client).

-export([do_local/2, do_remote/2]).

do_local(Pid, Request) ->
        case gen_server:call(Pid, Request) of
                {ok, Value} ->
                        Value;                                                      
                {error, Error} ->
                        exit(Error)                                    
        end.

do_remote(Pid, Request) ->
        gen_server:call(Pid, Request).

--------------------------------------------------------------------------
-module(foo_server).

-behaviour(gen_server).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
                        terminate/2, code_change/3]).

init(Arg) ->
        {ok, Arg}.

handle_call(foo_local, _From, State) ->
        {reply, {ok, 42}, State};
handle_call(foo_remote, _From, State) ->
        {reply, 42, State};
handle_call(bar_local, _From, State) ->
        {reply, {error, badarg}, State};
handle_call(bar_remote, {Pid, _Tag}, State) ->
        exit(Pid, badarg),
        {noreply, State}.

handle_cast(_Request, State) ->
        {noreply, State}.

handle_info(_Info, State) ->
        {noreply, State}.

terminate(_Reason, _State) ->
        ok.

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

--------------------------------------------------------------------------
1> application:start(sasl).
ok
2>  {ok, S} = gen_server:start(foo_server, [], []).
{ok,<0.45.0>}
3> foo_client:do_local(S, foo_local).
42
4> foo_client:do_local(S, bar_local).
** exception exit: badarg
     in function  foo_client:do_local/2
5> foo_client:do_remote(S, foo_remote).
42
6> foo_client:do_remote(S, bar_remote).
** exception exit: badarg
7>


On Fri, Nov 06, 2009 at 03:57:24PM +0000, Anton Krasovsky wrote:
}  What would be the standard way of returning/throwing an exception in a
}  result to a gen_server:call?
}  
}  Would something like that be reasonable:
}  
}  a() ->
}  case gen_server:call(..., a) of
}      {ok, Value} -> Value;
}      {error, Error} -> throw({error, Error})
}  end
}  
}  and
}  
}  handle_call(a, ...) ->
}  case ... of
}      {ok, Value} -> {reply, {ok, Value} , State};
}      {error, Error} -> {reply, {error, Error} , State}
}  end

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Anton Krasovsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

You're right, your approach is better.

Regards,
anton

On Fri, Nov 6, 2009 at 5:08 PM, David Mercer <dmercer@...> wrote:

> Is there any particular reason you want to throw the value rather than just
> erroring out?  If you need to throw it to a catch, I'd probably do as you
> say, but otherwise, I'd just do a match and let an error happen when it
> doesn't match.  Something like:
>
> a() ->
>        {ok, Value} = gen_server:call(..., a),
>        Value.
>
> Your handle_call looks OK to me.
>
> Cheers,
>
> David
>
>> -----Original Message-----
>> From: erlang-questions@... [mailto:erlang-questions@...] On
>> Behalf Of Anton Krasovsky
>> Sent: Friday, November 06, 2009 9:57 AM
>> To: erlang-questions@...
>> Subject: [erlang-questions] Exceptions and gen_server
>>
>> What would be the standard way of returning/throwing an exception in a
>> result to a gen_server:call?
>>
>> Would something like that be reasonable:
>>
>> a() ->
>> case gen_server:call(..., a) of
>>     {ok, Value} -> Value;
>>     {error, Error} -> throw({error, Error})
>> end
>>
>> and
>>
>> handle_call(a, ...) ->
>> case ... of
>>     {ok, Value} -> {reply, {ok, Value} , State};
>>     {error, Error} -> {reply, {error, Error} , State}
>> end
>>
>> Regards,
>> Anton
>>
>> ________________________________________________________________
>> erlang-questions mailing list. See http://www.erlang.org/faq.html
>> erlang-questions (at) erlang.org
>
>

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Anton Krasovsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I suppose you'd get better stack trace in my case, but also I didn't
realize you can do exit(Pid, Reason) to a caller process.

Anton

On Sat, Nov 7, 2009 at 9:03 PM, Vance Shipley <vances@...> wrote:

> Some time ago I was asking myself whether it was better to
> throw an exception in the client or the server.  In the end
> I decided your method was better because SASL handled it better.
>
>        -Vance
>
> --------------------------------------------------------------------------
> -module(foo_client).
>
> -export([do_local/2, do_remote/2]).
>
> do_local(Pid, Request) ->
>        case gen_server:call(Pid, Request) of
>                {ok, Value} ->
>                        Value;
>                {error, Error} ->
>                        exit(Error)
>        end.
>
> do_remote(Pid, Request) ->
>        gen_server:call(Pid, Request).
>
> --------------------------------------------------------------------------
> -module(foo_server).
>
> -behaviour(gen_server).
>
> -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
>                        terminate/2, code_change/3]).
>
> init(Arg) ->
>        {ok, Arg}.
>
> handle_call(foo_local, _From, State) ->
>        {reply, {ok, 42}, State};
> handle_call(foo_remote, _From, State) ->
>        {reply, 42, State};
> handle_call(bar_local, _From, State) ->
>        {reply, {error, badarg}, State};
> handle_call(bar_remote, {Pid, _Tag}, State) ->
>        exit(Pid, badarg),
>        {noreply, State}.
>
> handle_cast(_Request, State) ->
>        {noreply, State}.
>
> handle_info(_Info, State) ->
>        {noreply, State}.
>
> terminate(_Reason, _State) ->
>        ok.
>
> code_change(_OldVsn, State, _Extra) ->
>        {ok, State}.
>
> --------------------------------------------------------------------------
> 1> application:start(sasl).
> ok
> 2>  {ok, S} = gen_server:start(foo_server, [], []).
> {ok,<0.45.0>}
> 3> foo_client:do_local(S, foo_local).
> 42
> 4> foo_client:do_local(S, bar_local).
> ** exception exit: badarg
>     in function  foo_client:do_local/2
> 5> foo_client:do_remote(S, foo_remote).
> 42
> 6> foo_client:do_remote(S, bar_remote).
> ** exception exit: badarg
> 7>
>
>
> On Fri, Nov 06, 2009 at 03:57:24PM +0000, Anton Krasovsky wrote:
> }  What would be the standard way of returning/throwing an exception in a
> }  result to a gen_server:call?
> }
> }  Would something like that be reasonable:
> }
> }  a() ->
> }  case gen_server:call(..., a) of
> }      {ok, Value} -> Value;
> }      {error, Error} -> throw({error, Error})
> }  end
> }
> }  and
> }
> }  handle_call(a, ...) ->
> }  case ... of
> }      {ok, Value} -> {reply, {ok, Value} , State};
> }      {error, Error} -> {reply, {error, Error} , State}
> }  end
>

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Vance Shipley :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Sat, Nov 07, 2009 at 09:33:33PM +0000, Anton Krasovsky wrote:
}  I suppose you'd get better stack trace in my case, but also I didn't
}  realize you can do exit(Pid, Reason) to a caller process.

I do use this method to handle unknown calls in servers where I'd
rather the server kept running than have it fail and expose a
programming error in another process.

        handle_call(Request, From, State) ->
                ...
                {reply, Reply, State};
        handle_call(_Request, {Pid, _Tag}, State) ->
                exit(Pid, badarg),
                {noreply, State}.

This way if some process attempts gen_server:call(S, foo), where
foo is not a supported API call, they get the appropriate response.

--
        -Vance

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Anton Krasovsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

True, I'd normally just print unexpected requests, but it's better this way.

Anton

On Sat, Nov 7, 2009 at 9:42 PM, Vance Shipley <vances@...> wrote:

> On Sat, Nov 07, 2009 at 09:33:33PM +0000, Anton Krasovsky wrote:
> }  I suppose you'd get better stack trace in my case, but also I didn't
> }  realize you can do exit(Pid, Reason) to a caller process.
>
> I do use this method to handle unknown calls in servers where I'd
> rather the server kept running than have it fail and expose a
> programming error in another process.
>
>        handle_call(Request, From, State) ->
>                ...
>                {reply, Reply, State};
>        handle_call(_Request, {Pid, _Tag}, State) ->
>                exit(Pid, badarg),
>                {noreply, State}.
>
> This way if some process attempts gen_server:call(S, foo), where
> foo is not a supported API call, they get the appropriate response.
>
> --
>        -Vance
>

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org


Re: Exceptions and gen_server

by Robert Virding :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

There have been a number of different suggestions as to how you could
program this. I think the main thing for you to decide is *HOW* you want to
see the error, then it is relatively easy for you to program it. For
example:

- First off: do you want the error to crash the server or not? If so is
links and exit signals a good enough way for you to see the error?

- If the server is to live, how should your app see the error? Two valid
suggestions which have been given here are:

1. Signal the error using exit/2. This "simulates" the exit signals as if
the server had crashed.
2. Return an error value which either: returns an error value to the caller;
or generates an error using erlang:error/1. Which is best for you depends on
your app.

All these solutions are easy to program so the main problem is for you to
decide which is best for your app.

One thing I would *NOT* do is use throw/1 to signal error! Throw is for
non-local returns not errors. Using it for errors will just lead to
confusion and maybe errors if other parts of app assume it is a non-local
return. As try can differentiate between throw, exit and error it is
important that they are used for the right thing.

Robert

P.S. I would LOVE to change catch now so it only catches throws, but I don't
think it would be possible. :-(

2009/11/6 Anton Krasovsky <anton.krasovsky@...>

> What would be the standard way of returning/throwing an exception in a
> result to a gen_server:call?
>
> Would something like that be reasonable:
>
> a() ->
> case gen_server:call(..., a) of
>    {ok, Value} -> Value;
>    {error, Error} -> throw({error, Error})
> end
>
> and
>
> handle_call(a, ...) ->
> case ... of
>    {ok, Value} -> {reply, {ok, Value} , State};
>    {error, Error} -> {reply, {error, Error} , State}
> end
>
> Regards,
> Anton
>
> ________________________________________________________________
> erlang-questions mailing list. See http://www.erlang.org/faq.html
> erlang-questions (at) erlang.org
>
>

Re: Exceptions and gen_server

by Anton Krasovsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thanks for clarifying that, especially throw vs error part!


Regards,
Anton

On Sun, Nov 8, 2009 at 6:47 PM, Robert Virding <rvirding@...> wrote:

> There have been a number of different suggestions as to how you could
> program this. I think the main thing for you to decide is *HOW* you want to
> see the error, then it is relatively easy for you to program it. For
> example:
>
> - First off: do you want the error to crash the server or not? If so is
> links and exit signals a good enough way for you to see the error?
>
> - If the server is to live, how should your app see the error? Two valid
> suggestions which have been given here are:
>
> 1. Signal the error using exit/2. This "simulates" the exit signals as if
> the server had crashed.
> 2. Return an error value which either: returns an error value to the caller;
> or generates an error using erlang:error/1. Which is best for you depends on
> your app.
>
> All these solutions are easy to program so the main problem is for you to
> decide which is best for your app.
>
> One thing I would *NOT* do is use throw/1 to signal error! Throw is for
> non-local returns not errors. Using it for errors will just lead to
> confusion and maybe errors if other parts of app assume it is a non-local
> return. As try can differentiate between throw, exit and error it is
> important that they are used for the right thing.
>
> Robert
>
> P.S. I would LOVE to change catch now so it only catches throws, but I don't
> think it would be possible. :-(
>
> 2009/11/6 Anton Krasovsky <anton.krasovsky@...>
>>
>> What would be the standard way of returning/throwing an exception in a
>> result to a gen_server:call?
>>
>> Would something like that be reasonable:
>>
>> a() ->
>> case gen_server:call(..., a) of
>>    {ok, Value} -> Value;
>>    {error, Error} -> throw({error, Error})
>> end
>>
>> and
>>
>> handle_call(a, ...) ->
>> case ... of
>>    {ok, Value} -> {reply, {ok, Value} , State};
>>    {error, Error} -> {reply, {error, Error} , State}
>> end
>>
>> Regards,
>> Anton
>>
>> ________________________________________________________________
>> erlang-questions mailing list. See http://www.erlang.org/faq.html
>> erlang-questions (at) erlang.org
>>
>
>

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org