SV: Trouble with gen_server

Mark Lee mark@REDACTED
Wed Mar 29 10:57:20 CEST 2006


Bingo!

The bit I've been missing: catch,

thanks very much,

Mark

On Tue, Mar 28, 2006 at 07:36:01PM +0100, Richard Cameron wrote:
> 
> 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:
> 
> -module(s).
> -compile(export_all).
> -define(SERVER,srv).
> 
> 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}
> 		  end.
> 
> 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().
> {ok,<0.37.0>}
> 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().
> {ok,<0.37.0>}
> 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().
> {ok,<0.37.0>}
> 2> s:safe().
> {error,{timeout,{gen_server,call,[srv,slowcall,1]}}}
> 3> s:safe().
> {error,{timeout,{gen_server,call,[srv,slowcall,1]}}}
> 
> 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"  
> approach.
> 
> The OTP design principles document is probably the best starting  
> point for all this stuff:
> 
> http://erlang.se/doc/doc-5.4.12/doc/design_principles/part_frame.html
> 
> Richard.
> 
> !DSPAM:442982a6199691251019417!



More information about the erlang-questions mailing list