SV: Trouble with gen_server

Richard Cameron camster@REDACTED
Tue Mar 28 20:36:01 CEST 2006

On 28 Mar 2006, at 15:41, mark@REDACTED wrote:

> Ok, me being daft there. So how can I handle the timeout? I don't want
> the gen_server to die when the call exceeds the timeout, I just  
> want to
> be able to reflect that this has happened.

Have a look at this:


start() -> gen_server:start({local,?SERVER}, ?MODULE, [], []).
start_link() -> gen_server:start_link({local,?SERVER}, ?MODULE, [], []).
fail() -> gen_server:call(?SERVER, slowcall, 1).
safe() -> case catch fail() of
			  {'EXIT', Reason} -> {error, Reason};
			  Other -> {ok, Other}

init([]) -> {ok, []}.
handle_call(slowcall,_,State) ->
	receive after 100 -> x end,
	{reply, ok, State}.

There are a few ways of running it. Here's what you were doing:

Eshell V5.4.13  (abort with ^G)
1> s:start_link().
2> s:fail().
** exited: {timeout,{gen_server,call,[srv,slowcall,1]}} **
3> s:fail().
** exited: {noproc,{gen_server,call,[srv,slowcall,1]}} **

What's happening here is that you're using the shell to start your  
gen_server process. You end up linking the shell process to the  
gen_server process which means that if that the shell process dies,  
it takes down the gen_server with it. The default shell behaviour is  
that if it executes a function which fails, it exits and a new  
"replacement shell" process is spawned off to carry on. This is  
what's happening here. Your first call to s:fail() does just that -  
it fails. The gen_server was fine at that point... it didn't crash  
until it got brought down by the shell exiting (because the two  
processes are linked).

Here's another way of running the example:

1> s:start().
2> s:fail().
** exited: {timeout,{gen_server,call,[srv,slowcall,1]}} **
3> s:fail().
** exited: {timeout,{gen_server,call,[srv,slowcall,1]}} **

Again, the fail() function fails, but this time the shell process  
dying doesn't cause the gen_server to terminate. At the end of this  
example it's still alive and well and processing requests.

Another way is this:

Eshell V5.4.13  (abort with ^G)
1> s:start_link().
2> s:safe().
3> s:safe().

This time, although the shell process is linked to the gen_server,  
I've caught the error from the fail() function and so the shell  
didn't exit. Thus, it didn't cause the gen_server to die.

All three approaches are equally valid in different situations. Joe  
Armstrong's book (and his PhD thesis) gives examples of when you'd  
want to link processes together and when you wouldn't. Also, although  
there *is* a "catch" keyword in Erlang it's used surprisingly rarely.  
Catching all errors like this sometimes isn't actually terribly  
helpful, and it's certainly not something the language just makes you  
do to be awkward (religiously wrapping every possible things which  
could fail with a catch statement to prevent any knock-on effect for  
any processes linked to it - although I have seen Java programmers do  
things like this).

You need to have a strategy of which processes should die if a call  
to the gen_server doesn't succeed. There could be instances where you  
return a "Sorry, system can't cope with the load at the moment" to  
the end user and press on regardless. On the other hand you might be  
talking about a realtime system where the gen_server can't and  
shouldn't take that long to reply. If that's the case then you'd  
probably want to ask the gen_server to crash (crashing is a _good_  
thing in Erlang), shutdown cleanly, and then arrange for it to be  
restarted (by a supervisor) to get it into a decent state in order to  
continue processing requests. This is the Erlang "let it crash"  

The OTP design principles document is probably the best starting  
point for all this stuff:

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the erlang-questions mailing list