timely shutdown of timer threads

I have an application that forks a thread to run an activity on a timer. (The activity happens to be Berkeley DB checkpointing, but that's actually beside the point here.) The problem is that when the application wants to quit, I would like my main thread to be able to tell the timer thread to shut down in a timely way. However, I don't see a primitive in haskell that allows me to both wait for a timeout, or a notification. (If I were to do this in java, I would use wait/notify.) Here's the code I wrote to attempt this shutdown procedure: import Control.Exception import Database.Berkeley.Db import Data.IORef import System.Posix.Unistd ... closeEnv :: Env -> IO () closeEnv env = do threadId <- readIORef (envCheckpointThread env) case threadId of Just tid -> killThread tid Nothing -> return () dbEnv_close [] (envDbEnv env) startCheckpointing :: Env -> IO ThreadId startCheckpointing env = do forkOS run where run = catch checkpoint handler checkpoint = checkpoint1 >> checkpoint checkpoint1 = unblock $ do let dbenv = envDbEnv env _ <- sleep checkpointInterval putStrLn "# checkpoint" dbEnv_txn_checkpoint [] dbenv 0 0 handler ThreadKilled = return () handler exn = throw exn However, there are several problems here: First, the checkpointInterval is 15 sec, so it takes at least 15 seconds for the thread to wake up and be interrupted. Second, it isn't clear to me whether the killThread call should interrupt the sleep or not. In practice, several checkpoints occur before the ThreadKilled message is delivered, and this can take up to 2 minutes to shut down. Is there a better way to do this? Suggestions would be greatly appreciated, Warren

On 11-02-01 02:58 PM, Warren Harris wrote:
I have an application that forks a thread to run an activity on a timer. (The activity happens to be Berkeley DB checkpointing, but that's actually beside the point here.) The problem is that when the application wants to quit, I would like my main thread to be able to tell the timer thread to shut down in a timely way. However, I don't see a primitive in haskell that allows me to both wait for a timeout, or a notification. (If I were to do this in java, I would use wait/notify.)
Use an MVar for signalling; use a two-valued data type to represent time-to-work or time-to-die. For extra correctness, use a second MVar to be notified that the child thread is really done --- because otherwise there would be the race condition of the child thread still in the middle of critical I/O when the program quits. import Control.Concurrent import Control.Exception(finally) data DoOrDie = Do | Die main = do v <- newEmptyMVar finale <- newEmptyMVar forkIO (timeloop v `finally` putMVar finale ()) putStrLn "press enter to quit" getLine putMVar v Die takeMVar finale timeloop v = run where run = do forkIO (threadDelay 15000000 >> putMVar v Do) m <- takeMVar v case m of Do -> putStrLn "checkpoint" >> run Die -> putStrLn "checkmate"

Interesting. I hadn't thought of this solution. You're forking the timer to yet a third thread so that if it continues waiting beyond the checkpoint thread shutdown it doesn't really matter. I guess that works as long as the main thread doesn't wait for all other threads to terminate before terminating the app. It still seems to me that haskell is lacking when it comes to operations that can wait for multiple conditions. Warren On Feb 1, 2011, at 6:25 PM, Albert Y. C. Lai wrote:
On 11-02-01 02:58 PM, Warren Harris wrote:
I have an application that forks a thread to run an activity on a timer. (The activity happens to be Berkeley DB checkpointing, but that's actually beside the point here.) The problem is that when the application wants to quit, I would like my main thread to be able to tell the timer thread to shut down in a timely way. However, I don't see a primitive in haskell that allows me to both wait for a timeout, or a notification. (If I were to do this in java, I would use wait/notify.)
Use an MVar for signalling; use a two-valued data type to represent time-to-work or time-to-die. For extra correctness, use a second MVar to be notified that the child thread is really done --- because otherwise there would be the race condition of the child thread still in the middle of critical I/O when the program quits.
import Control.Concurrent import Control.Exception(finally)
data DoOrDie = Do | Die
main = do v <- newEmptyMVar finale <- newEmptyMVar forkIO (timeloop v `finally` putMVar finale ()) putStrLn "press enter to quit" getLine putMVar v Die takeMVar finale
timeloop v = run where run = do forkIO (threadDelay 15000000 >> putMVar v Do) m <- takeMVar v case m of Do -> putStrLn "checkpoint" >> run Die -> putStrLn "checkmate"
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Wed, Feb 2, 2011 at 10:42 PM, Warren Harris
Interesting. I hadn't thought of this solution. You're forking the timer to yet a third thread so that if it continues waiting beyond the checkpoint thread shutdown it doesn't really matter. I guess that works as long as the main thread doesn't wait for all other threads to terminate before terminating the app.
It still seems to me that haskell is lacking when it comes to operations that can wait for multiple conditions.
I think we can make waiting for both I/O activity and timers at the same time using the I/O manager. I will need to do this for my planned timeout support in the network package. Johan

On Feb 2, 2011, at 2:02 PM, Johan Tibell wrote:
On Wed, Feb 2, 2011 at 10:42 PM, Warren Harris
wrote: Interesting. I hadn't thought of this solution. You're forking the timer to yet a third thread so that if it continues waiting beyond the checkpoint thread shutdown it doesn't really matter. I guess that works as long as the main thread doesn't wait for all other threads to terminate before terminating the app.
It still seems to me that haskell is lacking when it comes to operations that can wait for multiple conditions.
I think we can make waiting for both I/O activity and timers at the same time using the I/O manager. I will need to do this for my planned timeout support in the network package.
I could see wanting to wait for an MVar, timeout, IO activity, STM channels (anything that could block), all in the same event handler. (Rather like the old WaitForMultipleObjects on Windows.) This would also require a way to test each condition without blocking (MVar has this with tryTakeMVar, but it doesn't look like they all support it.) Warren

On 11-02-02 04:42 PM, Warren Harris wrote:
It still seems to me that haskell is lacking when it comes to operations that can wait for multiple conditions.
STM opens the avenue to waiting for multiple conditions. import Control.Concurrent import Control.Concurrent.STM import Control.Exception(finally) data DoOrDie = Do | Die main = do die <- atomically newEmptyTMVar finale <- atomically newEmptyTMVar forkIO (timeloop die `finally` atomically (putTMVar finale ())) putStrLn "press enter to quit" getLine atomically (putTMVar die Die) atomically (takeTMVar finale) timeloop die = run where run = do w <- atomically newEmptyTMVar i <- forkIO (threadDelay 15000000 >> atomically (putTMVar w Do)) r <- atomically (takeTMVar w `orElse` takeTMVar die) case r of Do -> putStrLn "checkpoint" >> run Die -> killThread i >> putStrLn "checkmate"
participants (3)
-
Albert Y. C. Lai
-
Johan Tibell
-
Warren Harris