What is the surefire way to handle all exceptions and make sure the program doesn't fail?

First of all, apologise if the question is too broad. The background goes like this: I've implemented a server program in Haskell for my company intended to replace the previous one written in C which crashes a lot (and btw the technology of the company is exclusively C-based). When I chose Haskell I promised my manager (arrogantly - I actually made a bet with him), "it won't crash". Now it has been finished (with just a few hundred LOC), and my test shows that it is indeed very stable. But by looking at the code again I'm a little worried, since I'm rather new to exception handling and there're many networking-related functions in the program. I was tempted to catch (SomeException e) at the very top-level of the program and try to recursively call main to restart the server in case of any exception being thrown, but I highly doubt that is the correct and idiomatic way. There are also a number of long-running threads launched from the main thread, and exceptions thrown from these threads can't be caught by the top-level `catch' in the main thread. My main function looks like this: main :: IO () main = withSocketsDo $ do sCameraU <- socketNewPassive False 6000 sStunU <- socketNewPassive False 3478 sCmdT <- socketNewPassive True 7000 mvarCam <- newMVar M.empty mvarLog <- newMVar [] forkIO $ regCamera sCameraU mvarCam mvarLog forkIO $ updCamera mvarCam mvarLog forkIO $ stun sCameraU sStunU mvarCam mvarLog listen sCmdT 128 processCmd sCmdT mvarCam mvarLog sClose sCameraU sClose sStunU sClose sCmdT I find that I can't tell whether a function will throw any exception at all, or what exceptions will be thrown, by looking at their documentation. I can only tell if I browse the source code. So the question is, how can I determine all the exceptions that can be thrown by a given function? And what is the best way to handle situations like this, with both the long-running threads and main thread need to be restarted whenever exceptions happen. Regards, Yifan

On 07/17/2012 08:34 AM, Yifan Yu wrote:
First of all, apologise if the question is too broad. The background goes like this: I've implemented a server program in Haskell for my company intended to replace the previous one written in C which crashes a lot (and btw the technology of the company is exclusively C-based). When I chose Haskell I promised my manager (arrogantly - I actually made a bet with him), "it won't crash". Now it has been finished (with just a few hundred LOC), and my test shows that it is indeed very stable. But by looking at the code again I'm a little worried, since I'm rather new to exception handling and there're many networking-related functions in the program. I was tempted to catch (SomeException e) at the very top-level of the program and try to recursively call main to restart the server in case of any exception being thrown, but I highly doubt that is the correct and idiomatic way. There are also a number of long-running threads launched from the main thread, and exceptions thrown from these threads can't be caught by the top-level `catch' in the main thread. My main function looks like this:
[--snip--]
I find that I can't tell whether a function will throw any exception at all, or what exceptions will be thrown, by looking at their documentation. I can only tell if I browse the source code. So the question is, how can I determine all the exceptions that can be thrown by a given function?
Look at its source.
And what is the best way to handle situations like this, with both the long-running threads and main thread need to be restarted whenever exceptions happen.
The most robust way is probably to use a completely independent supervisor program, e.g. "upstart", "systemd", "runit", etc. These usually have facilities for restarting the supervised program, and a rate limit on exactly how often to try that (over a given period of time). These *won't* work for a program that's deadlocked because an important thread has died. For that you'll need either a watchdog (external) or an in-program mechanism for "supervised threads" which can catch any and all exceptions and restart threads as necessary. This tends to very domain-specific, but you might take some inspiration for the way supervisor hierarchies work in the actor model. Regards,

On 17 July 2012 22:10, Bardur Arantsson
On 07/17/2012 08:34 AM, Yifan Yu wrote:
I can only tell if I browse the source code. So the question is, how can I determine all the exceptions that can be thrown by a given function?
Look at its source.
Not sure that's the most productive comment. ;-P

On 07/17/2012 10:17 PM, Christopher Done wrote:
On 17 July 2012 22:10, Bardur Arantsson
wrote: On 07/17/2012 08:34 AM, Yifan Yu wrote:
I can only tell if I browse the source code. So the question is, how can I determine all the exceptions that can be thrown by a given function?
Look at its source.
Not sure that's the most productive comment. ;-P
Well, it's either that or the documentation, and if you want to be *really* sure... (I did realize that the OP did mention looking at the source, I just thought I'd confirm. I hope it didn't come out snarky -- I certainly didn't intend it to.) Regards,

On Wed, Jul 18, 2012 at 4:10 AM, Bardur Arantsson
The most robust way is probably to use a completely independent supervisor program, e.g. "upstart", "systemd", "runit", etc. These usually have facilities for restarting the supervised program, and a rate limit on exactly how often to try that (over a given period of time).
These *won't* work for a program that's deadlocked because an important thread has died. For that you'll need either a watchdog (external) or an in-program mechanism for "supervised threads" which can catch any and all exceptions and restart threads as necessary. This tends to very domain-specific, but you might take some inspiration for the way supervisor hierarchies work in the actor model.
Hi Bardur, the "supervised threads" sounds like a good approach for me. Thanks!

Hello there Yifan,
exception handling should be done on a per-context basis, where the
developer establishes the notion of context. Most of the time this
boils down to releasing resources:
forkIO (doStuffWith h `finally` hClose h)
In more complicated scenarios, where you actually need to /handle/ the
exception you should probably wrap some control concept around it.
There are many options. You could just catch and handle the exception.
Other options include a resumable monad (like monad-coroutine) that
brings everything back into a consistent state.
Exception handling is convenient in Haskell. You should probably just
try to enforce some of the exception cases by using the server in a
wrong way. Close the connection prematurely or send Unix signals. Note
that you need to handle signals separately. In particular by default a
SIGPIPE, which can in fact be thrown by the networking system, needs to
be ignored:
import System.Posix.Signal
main :: IO ()
main =
withSocketsDo $ do
installHandler sigPIPE Ignore Nothing
Finally for both efficiency and safety make use of a stream processing
abstraction like conduit, enumerator or pipes.
Greets,
Ertugrul
Yifan Yu
First of all, apologise if the question is too broad. The background goes like this: I've implemented a server program in Haskell for my company intended to replace the previous one written in C which crashes a lot (and btw the technology of the company is exclusively C-based). When I chose Haskell I promised my manager (arrogantly - I actually made a bet with him), "it won't crash". Now it has been finished (with just a few hundred LOC), and my test shows that it is indeed very stable. But by looking at the code again I'm a little worried, since I'm rather new to exception handling and there're many networking-related functions in the program. I was tempted to catch (SomeException e) at the very top-level of the program and try to recursively call main to restart the server in case of any exception being thrown, but I highly doubt that is the correct and idiomatic way. There are also a number of long-running threads launched from the main thread, and exceptions thrown from these threads can't be caught by the top-level `catch' in the main thread. My main function looks like this:
main :: IO () main = withSocketsDo $ do sCameraU <- socketNewPassive False 6000 sStunU <- socketNewPassive False 3478 sCmdT <- socketNewPassive True 7000 mvarCam <- newMVar M.empty mvarLog <- newMVar []
forkIO $ regCamera sCameraU mvarCam mvarLog forkIO $ updCamera mvarCam mvarLog forkIO $ stun sCameraU sStunU mvarCam mvarLog
listen sCmdT 128 processCmd sCmdT mvarCam mvarLog
sClose sCameraU sClose sStunU sClose sCmdT
I find that I can't tell whether a function will throw any exception at all, or what exceptions will be thrown, by looking at their documentation. I can only tell if I browse the source code. So the question is, how can I determine all the exceptions that can be thrown by a given function? And what is the best way to handle situations like this, with both the long-running threads and main thread need to be restarted whenever exceptions happen.
-- Not to be or to be and (not to be or to be and (not to be or to be and (not to be or to be and ... that is the list monad.

On Wed, Jul 18, 2012 at 7:05 AM, Ertugrul Söylemez
exception handling should be done on a per-context basis, where the developer establishes the notion of context. Most of the time this boils down to releasing resources:
forkIO (doStuffWith h `finally` hClose h)
Hello Ertugrul, Agreed, although sometimes I just want to be lazy and catch any exception and see what it is in the top-level context :-)
In more complicated scenarios, where you actually need to /handle/ the exception you should probably wrap some control concept around it. There are many options. You could just catch and handle the exception. Other options include a resumable monad (like monad-coroutine) that brings everything back into a consistent state.
Finally for both efficiency and safety make use of a stream processing abstraction like conduit, enumerator or pipes.
Thank you for these interesting pointers, I'll look into them later.
participants (4)
-
Bardur Arantsson
-
Christopher Done
-
Ertugrul Söylemez
-
Yifan Yu