[erlang-bugs] gen_server error report issue

Aleksander Nycz Aleksander.Nycz@REDACTED
Sat Apr 16 07:33:53 CEST 2016


Hello,

I think I've found some problem in gen_server error report:

when terminate callback throw errors then earlier errors (eg. from 
handle_call callback) is not reported.

Please compile and run provided simple_server.erl module, that implement 
gen_server behaviour:

(compilation12836@REDACTED)4> simple_server:start_link(false).
{ok,<0.128.0>}
(compilation12836@REDACTED)5> simple_server:perform(false).
ok
(compilation12836@REDACTED)6> simple_server:perform(true).
** exception exit: perform_error_in_handle_call
*in function  simple_server:handle_call/3 
(loadController/src/simple_server.erl, line 89)*
      in call from gen_server:try_handle_call/4 (gen_server.erl, line 629)
      in call from gen_server:handle_msg/5 (gen_server.erl, line 661)
      in call from proc_lib:init_p_do_apply/3 (proc_lib.erl, line 240)
(compilation12836@REDACTED)7>
=ERROR REPORT==== 16-Mar-2016::15:36:35 ===
** Generic server simple_server terminating
** Last message in was {perform,true}
** When Server state == {state,false}
** Reason for termination ==
** {perform_error_in_handle_call,
        [{simple_server,handle_call,3,
[{file,"loadController/src/simple_server.erl"},{line,89}]},
{gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,240}]}]}

In this case we can see that exception was thrown 'in function 
simple_server:handle_call/3 (loadController/src/simple_server.erl, line 89)'

And now please start server in this way:

(compilation12836@REDACTED)7> simple_server:start_link(true).
{ok,<0.133.0>}
(compilation12836@REDACTED)8> simple_server:perform(false).
ok
(compilation12836@REDACTED)9> simple_server:perform(true).

=ERROR REPORT==== 16-Mar-2016::15:37:30 ===
** Generic server simple_server terminating
** Last message in was {perform,true}
** When Server state == {state,true}
** Reason for termination ==
** {throw_error_in_terminate_callback,
        [{simple_server,terminate,2,
[{file,"loadController/src/simple_server.erl"},{line,141}]},
{gen_server,try_terminate,3,[{file,"gen_server.erl"},{line,643}]},
{gen_server,terminate,7,[{file,"gen_server.erl"},{line,809}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,240}]}]}
** exception exit: throw_error_in_terminate_callback
      in function  simple_server:terminate/2 
(loadController/src/simple_server.erl, line 141)
      in call from gen_server:try_terminate/3 (gen_server.erl, line 643)
      in call from gen_server:terminate/7 (gen_server.erl, line 809)
      in call from proc_lib:init_p_do_apply/3 (proc_lib.erl, line 240)

Now gen_server is also terminating, but we can only see exception from 
terminate callback.

Exception that causes gen_server termination is not reported.
I rather expect that both exceptions will be shown.


Regards
Aleksander Nycz


-- 
Aleksander Nycz
Chief Designer
Telco_021 BSS R&D
Comarch SA
Phone:  +48 17 785 5909
Mobile: +48 691 464 275
website:www.comarch.pl

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-bugs/attachments/20160416/edce0298/attachment.htm>
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% @author ANycz
%%% @copyright (C) 2016, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 16. mar 2016 15:30
%%%-------------------------------------------------------------------
-module(simple_server).
-author("ANycz").

-behaviour(gen_server).

%% API
-export([start_link/1, perform/1]).

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

-define(SERVER, ?MODULE).

-record(state, {throw_error_in_terminate_callback :: boolean()}).

%%%===================================================================
%%% API
%%%===================================================================


perform(ThrowError) ->
    gen_server:call(?MODULE, {perform, ThrowError})
.

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link(ThrowError::boolean) ->
    {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link(ThrowError) ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, ThrowError, []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
    {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term()} | ignore).
init(ThrowError) ->
    {ok, #state{throw_error_in_terminate_callback = ThrowError}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #state{}) ->
    {reply, Reply :: term(), NewState :: #state{}} |
    {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
    {noreply, NewState :: #state{}} |
    {noreply, NewState :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
    {stop, Reason :: term(), NewState :: #state{}}).

handle_call({perform, false}, _From, State) ->
    {reply, ok, State};
handle_call({perform, true}, _From, State) ->
    erlang:error(perform_error_in_handle_call),
    {reply, ok, State};
handle_call(_Request, _From, State) ->
    {reply, ok, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #state{}) ->
    {noreply, NewState :: #state{}} |
    {noreply, NewState :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #state{}}).
handle_cast(_Request, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
    {noreply, NewState :: #state{}} |
    {noreply, NewState :: #state{}, timeout() | hibernate} |
    {stop, Reason :: term(), NewState :: #state{}}).
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
    State :: #state{}) -> term()).
terminate(_Reason, #state{throw_error_in_terminate_callback = false}) ->
    ok;
terminate(_Reason, #state{throw_error_in_terminate_callback = true}) ->
    erlang:error(throw_error_in_terminate_callback),
    ok.


%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
    Extra :: term()) ->
    {ok, NewState :: #state{}} | {error, Reason :: term()}).
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4936 bytes
Desc: Kryptograficzna sygnatura S/MIME
URL: <http://erlang.org/pipermail/erlang-bugs/attachments/20160416/edce0298/attachment.bin>


More information about the erlang-bugs mailing list