1{-# LANGUAGE DeriveDataTypeable #-}
3module System.TimeManager (
25import Control.Concurrent (myThreadId)
26import qualified Control.Exception as E
28import Data.Typeable (Typeable)
29import Data.IORef (IORef)
30import qualified Data.IORef as I
32----------------------------------------------------------------
35type Manager = Reaper [Handle] Handle
37-- | An action to be performed on timeout.
38type TimeoutAction = IO ()
40-- | A handle used by 'Manager'
41data Handle = Handle !(IORef TimeoutAction) !(IORef State)
43data State = Active -- Manager turns it to Inactive.
44 | Inactive -- Manager removes it with timeout action.
45 | Paused -- Manager does not change it.
46 | Canceled -- Manager removes it without timeout action.
48----------------------------------------------------------------
50-- | Creating timeout manager which works every N micro seconds
51-- where N is the first argument.
52initialize :: Int -> IO Manager
53initialize timeout = mkReaper defaultReaperSettings
54 { reaperAction = mkListAction prune
55 , reaperDelay = timeout
58 prune m@(Handle actionRef stateRef) = do
59 state <- I.atomicModifyIORef' stateRef (\x -> (inactivate x, x))
62 onTimeout <- I.readIORef actionRef
63 onTimeout `E.catch` ignoreAll
65 Canceled -> return Nothing
68 inactivate Active = Inactive
71----------------------------------------------------------------
73-- | Stopping timeout manager with onTimeout fired.
74stopManager :: Manager -> IO ()
75stopManager mgr = E.mask_ (reaperStop mgr >>= mapM_ fire)
77 fire (Handle actionRef _) = do
78 onTimeout <- I.readIORef actionRef
79 onTimeout `E.catch` ignoreAll
81ignoreAll :: E.SomeException -> IO ()
82ignoreAll _ = return ()
84-- | Killing timeout manager immediately without firing onTimeout.
85killManager :: Manager -> IO ()
86killManager = reaperKill
88----------------------------------------------------------------
90-- | Registering a timeout action.
91register :: Manager -> TimeoutAction -> IO Handle
92register mgr onTimeout = do
93 actionRef <- I.newIORef onTimeout
94 stateRef <- I.newIORef Active
95 let h = Handle actionRef stateRef
99-- | Registering a timeout action of killing this thread.
100registerKillThread :: Manager -> TimeoutAction -> IO Handle
101registerKillThread m onTimeout = do
102 -- If we hold ThreadId, the stack and data of the thread is leaked.
103 -- If we hold Weak ThreadId, the stack is released. However, its
104 -- data is still leaked probably because of a bug of GHC.
105 -- So, let's just use ThreadId and release ThreadId by
106 -- overriding the timeout action by "cancel".
108 -- First run the timeout action in case the child thread is masked.
109 register m $ onTimeout `E.finally` E.throwTo tid TimeoutThread
111data TimeoutThread = TimeoutThread
113instance E.Exception TimeoutThread where
114 toException = E.asyncExceptionToException
115 fromException = E.asyncExceptionFromException
116instance Show TimeoutThread where
117 show TimeoutThread = "Thread killed by timeout manager"
119----------------------------------------------------------------
121-- | Setting the state to active.
122-- 'Manager' turns active to inactive repeatedly.
123tickle :: Handle -> IO ()
124tickle (Handle _ stateRef) = I.writeIORef stateRef Active
126-- | Setting the state to canceled.
127-- 'Manager' eventually removes this without timeout action.
128cancel :: Handle -> IO ()
129cancel (Handle actionRef stateRef) = do
130 I.writeIORef actionRef (return ()) -- ensuring to release ThreadId
131 I.writeIORef stateRef Canceled
133-- | Setting the state to paused.
134-- 'Manager' does not change the value.
135pause :: Handle -> IO ()
136pause (Handle _ stateRef) = I.writeIORef stateRef Paused
138-- | Setting the paused state to active.
139-- This is an alias to 'tickle'.
140resume :: Handle -> IO ()
143----------------------------------------------------------------
145-- | Call the inner function with a timeout manager.
146withManager :: Int -- ^ timeout in microseconds
149withManager timeout f = do
150 -- FIXME when stopManager is available, use it
151 man <- initialize timeout
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list