
On Fri, Apr 07, 2006 at 02:58:01PM +0100, Simon Marlow wrote:
Of course you could implement some global flag to say that an exit is in progress, but that implies explicit checking of the flag all over the place, which is what asynchronous exceptions are designed to avoid.
When *do* we exit, in fact? When all the exit handlers have finished?
I think we might be thinking of different things. here is a complete implementation of exit. exitMVar :: MVar () -- starts full exitMVar = .. handlerMVar :: MVar [IO ()] -- starts with [] handlerMVar = ... onExit :: IO () -> IO () onExit action = modifyMVar handlerMVar (action:) exitWith status = do takeMVar exitMVar -- winner takes all let handleLoop = do hs <- swapMVar handlerMVar [] sequence_ hs if null hs then return () else handleLoop handleLoop exitWith_ status exitWith_ calls the underlying 'exit' routine of the operating system immediatly. no waiting. I'll get to why you can't have handlers building up indefinitly below.
I think you have that backwards, releasing resources is the right thing to do when you get an exception, but there are lots of other reasons you want to release resources that have nothing to do with exceptions. you don't use 'throwTo' to close all your files :)
No, but you do use an exception handler, or something built using exception handlers like 'finally'. I don't want to have to use *both* exception handlers and exit handlers.
they serve different purposes. You might use both at different places in the same program, but never for the same resource.
The situation is the same as in your proposal - the foreign call continues running. However, as soon as it returns, the Haskell thread will receive an exception.
I propose this:
When System.Exit.exitWith is called, all currently running threads are sent an exit exception as soon as possible. Exit handlers registered with onExit are started immediately. The system exits when (a) the main thread has stopped, and (b) all exit handlers have completed. Subsequent calls to exitWith simply throw an exit exception in the current thread.
this seems the wrong way round. exitWith is something you call in _response_ to an exception, telling the program you want to exit. not something that generates an exception. In particular, you often won't know what 'status' to exit with until you have had everything clean up properly (or fail to clean up properly). We have 'throwTo' to throw exceptions around. what I would expect from dealing with other languages is: exitWith does as it does above in my example, nothing more, nothing less. in particular it is not special in any way when it comes to exceptions or concurrency other than using standard MVars. falling off the end of the main thread is equivalent to calling exitWith Success, an exception falling off the end is equivalent to exitWith Failure. the main thread is not special in any way other than being wrappen in the equivalent of. -- user written main function main = do ... -- what the implementation uses as its main thread realMain = catch (\_ -> exitFailure) main >> exitSuccess if you want to die and clean up via exceptions, use 'throwTo' to throw a 'PleaseExit' exception to whatever threads you like. if you are writing a library that uses threads internally, where you have a particular thread you want to clean up via exceptions, do an myThreadId >>= onExit . throwTo PleaseExit now you will get an exception on exit. if you need the exit to wait until you complete something, you can have your handler wait on an MVar. advantages of this set up. 1. base case requires no concurrency or exceptions 2. abstract threads possible, if you don't let your ThreadId escape, there is no way to get an exception you don't bring upon yourself. 3. simple rules. expressable in pure haskell. 4. can quit immediatly on a SIGINT since the exitWith routine runs on whatever thread called exit, rather than throwing responsibility back to the other threads which might be stuck in a foreign call. (unless you explicitly ask it to) 5. you don't have to worry about 'PleaseExit' if you don't want to. 6. modularity modularity. now that concurrency is part of the standard, we will likely see a lot of libraries using concurrency internally for little things that it wants to keep abstract, or concurrent programs composed with each other. having a global 'throw something to all threads on the system' doesn't feel right. 7. subsumes the exitWith throws exceptions everywhere policy. John -- John Meacham - ⑆repetae.net⑆john⑈