[Haskell-beginners] Interrupting a thread
Dean Herington
heringtonlacey at mindspring.com
Thu Dec 31 02:21:02 EST 2009
At 6:08 PM -0600 12/30/09, Floptical Logic wrote:
>On Sun, Dec 27, 2009 at 1:47 PM, Stephen Blackheath [to
>Haskell-Beginners] <mutilating.cauliflowers.stephen at blacksapphire.com>
>wrote:
>> MVars are the lowest-level operation for this kind of thing in Haskell,
>> and they're very fast. Anything can be done with MVars but in some cases
>> you need extra worker threads (cheap in Haskell), and you may even need
>> to kill threads (which is a safe operation in Haskell). CHP is higher
>> level and designed for this sort of complexity, so you might want to
>> look at that. I'll give you the answer I know, which is a low-level
>> MVar answer.
>>
>> I have *not* tried compiling this code.
>>
>>
>> import Control.Concurrent
>> import Control.Concurrent.MVar
>> import Data.Int
>> import System.Time
>>
>> type Microseconds = Int64
>>
>> getSystemTime :: IO Microseconds
>> getSystemTime = do
>> (TOD sec pico) <- getClockTime
>> return $!
>> (fromIntegral sec::Int64) * 1000000 +
>> (fromIntegral pico::Int64) `div` 1000000
>>
>> type Stack a = [a] -- or whatever type you want
>>
>> isEmpty :: Stack a -> Bool
>> isEmpty [] = True
>> isEmpty _ = False
>>
>> pop :: Stack a -> (a, Stack a)
>>
>> data ScheduleInput = ModifyStack (Stack -> Stack) | WaitFor Microseconds
>> | Timeout
>>
>> never = maxBound :: Microseconds
>>
>> schedule :: MVar ScheduleInput -> MVar a -> Stack a -> IO ()
>> schedule inpVar wnVar stack = schedule_ never stack
>> where
>> schedule_ :: Microseconds -> Stack -> IO ()
>> schedule_ timeout stack = do
>> now <- getSystemTime
>> let tillTimeout = 0 `max` (timeout - now)
>> if tillTimeout == 0 && not (isEmpty stack) then do
>> let (val, stack') = pop stack
>> putMVar wnVar (PopValue val)
>> schedule never stack'
>> else do
>> inp <- takeMVarWithTimeout (fromIntegral tillTimeout) inpVar
>> case inp of
>> ModifyStack f -> schedule_ timeout (f stack)
>> WaitFor t -> do
>> now <- getSystemTime
>> schedule (t+now) stack
>> Timeout -> schedule timeout stack
>>
>> readMVarWithTimeout :: Int -> MVar ScheduleInput -> IO ScheduleInput
>> readMVar timeoutUS inpVar = do
>> tid <- forkIO $ do
>> threadDelay timeoutUS
>> putMVar inpVar Timeout
>> inp <- takeMVar inpVar
>> killThread tid
>> return inp
>>
>> waitNotify :: MVar ScheduleInput -> MVar Int -> IO ()
>> waitNotify schInp wnInp = do
>> val <- takeMVar wnInp
>> ...notify...
>> let t = ....
>> putMVar schInp $ WaitFor t -- block input for the specified period
>>
>> main = do
>> schVar <- newEmptyMVar
>> wnVar <- newEmptyMVar
>> forkIO $ schedule schVar wnVar []
>> forkIO $ waitNotify wnVar schVar
>> ...
>> -- Modify stack according to user input inside your main IO loop
>> putMVar schVar $ ModifyStack $ \stack -> ...
>>
>>
>> I'm sure this is not exactly what you want, but at least it illustrates
>> how you can achieve anything you like by using MVars + extra worker
>> threads + killing threads (useful for implementing timeouts).
>>
>>
>> Steve
>>
>> Floptical Logic wrote:
>>> Hi,
>>>
>>> I am new to concurrency in Haskell and I am having trouble
>>> implementing the notion of interrupting a thread.
>>>
>>> In a new thread, call it waitNotify, I am trying to do the following:
>>> pop a number from a stack, wait some number of seconds based on the
>>> number popped from the stack, perform some notification, and repeat
>>> until there are no more numbers in the stack at which point we wait
>>> for a new number.
>>>
>>> These numbers will be supplied interactively by the user from main.
>>> When the user supplies a new number, I want to interrupt whatever
>>> waiting is happening in waitNotify, insert the number in the proper
> >> position in the current stack, and resume waitNotify using the updated
>>> stack. Note, here "stack" is just a generalization; it will likely
>>> just be a list.
>>>
>>> What is the most idiomatic way to capture this sort of behavior in
>>> Haskell? My two challenges are the notion of interrupting a thread,
>>> and sharing and updating this stack between threads (main and
>>> waitNotify).
>>>
>>> Thank you
>>> _______________________________________________
>>> Beginners mailing list
>>> Beginners at haskell.org
>>> http://www.haskell.org/mailman/listinfo/beginners
>>>
>> _______________________________________________
>> Beginners mailing list
>> Beginners at haskell.org
>> http://www.haskell.org/mailman/listinfo/beginners
>>
>
>Thanks Stephen. That worked and things are starting to make more
>sense. The crux of it all was the readMVarWithTimeout function which
>does exactly what I want.
>
>Now I want to extend this a bit further to use StateT. I've modified
>the schedule function quite a bit, and now it has type schedule ::
>StateT MyState IO. I keep the MVar actVar in MyState as well the list
>(stack) of wait times. However, the type of forkIO is forkIO :: IO ()
>-> IO ThreadId. How do I fork a new thread for schedule even though
>it is wrapped in the State monad?
>
>Thanks again.
You'll want something like this:
myFork :: StateT MyState IO () -> MyState -> StateT MyState IO ThreadId
myFork action initialState = liftIO (forkIO (evalStateT action initialState))
Dean
More information about the Beginners
mailing list