On Sat, Dec 03, 2005 at 10:35:54PM +0100, Tomasz Zielonka wrote:
> so I could export a waitUntil function with type
> 
>     waitUntil :: Time -> STM ()
> 
> but I tripped on something that was reported before, namely that STM
> transactions can't be nested (as a result of unsafePerformIO or
> unsafeInterleaveIO). Is there a plan to support such scenario?

OK, it can be worked around by running the atomically block in a new
transaction. But still it would be nice if the program didn't segfault,
but cause an exception for example.

Updated modules attached.

Best regards
Tomasz

-- 
I am searching for a programmer who is good at least in some of
[Haskell, ML, C++, Linux, FreeBSD, math] for work in Warsaw, Poland
module Main where

import Debug.Trace
import Control.Concurrent.STM
import Control.Concurrent
import Control.Monad
import TimeVar
import Random
import Control.Exception (finally)

main = do
    runTests
        [ test 1
        , testMany
        , test 5
        , test 10
        ]

runTests fs = do
    mapM_ (\t -> t >> putStrLn "") fs

test secs = do
    putStrLn ("Testing waiting for " ++ show secs ++ " seconds")
    -- waitUntil <- liftM waitOnTimeVar initTimeVar
    time1 <- getTime
    atomically $ do
        stmTrace "try"
        waitUntil (time1 + secs)
    time2 <- getTime
    putStrLn $ concat $
        [ show time2
        , " - "
        , show time1
        , " = "
        , show (time2 - time1)
        ]

testMany = do
    putStrLn "Testing with many threads"
    messages <- atomically newTChan
    threadCount <- atomically (newTVar 0)
    let forkIO' t = do
            atomically (modifyTVar threadCount succ)
            forkIO (t `finally` atomically (modifyTVar threadCount pred))
    replicateM 30 $ forkIO' $ do
        secs <- liftM fromIntegral (randomRIO (1 :: Int, 10))
        time1 <- getTime
        atomically $ do
            waitUntil (time1 + secs)
        time2 <- getTime
        atomically $ writeTChan messages $ concat $
            [ "waited for "
            , show (time2 - time1)
            , " seconds, difference from requested: "
            , show ((time2 - time1) - secs)
            ]
    let loop = do
            join $ atomically $
                (do msg <- readTChan messages
                    return (putStrLn msg >> loop))
              `orElse`
                (do n <- readTVar threadCount
                    guard (n == 0)
                    return (return ()))
    loop

--------------------------------------------------------------------------------

stmTrace s = do
    v <- newTVar ()
    trace s v `seq` return ()

modifyTVar v f = readTVar v >>= writeTVar v . f

module TimeVar
    ( Time
    , getTime
    , waitUntil
    , timeVar
    ) where

import Control.Concurrent.STM
import Control.Concurrent
import Control.Monad
import System.Time (getClockTime, ClockTime(..))
import Data.Bits
import Data.Int
import System.IO.Unsafe (unsafePerformIO)

newtype TimeVar = TimeVar [TVar Bool]

{-# NOINLINE timeVar #-}
timeVar :: TimeVar
timeVar = unsafePerformIO $ do
    var <- newEmptyMVar
    forkIO $ do
        tv <- initTimeVar
        putMVar var tv
    takeMVar var

waitUntil :: Time -> STM ()
waitUntil = waitOnTimeVar timeVar

type Time = Int32

getTime :: IO Time
getTime = do
    TOD secs _ <- getClockTime
    return (fromIntegral secs)

initTimeVar :: IO TimeVar
initTimeVar = do
    t0 <- liftM toBitsFromMSB getTime
    vars <- atomically $ do
        sequence
            [ do
                v <- newTVar b
                return v
            | b <- t0 ]
    forkIO $ do
        sequence_ $ repeat $ do
            threadDelay 100000
            t <- liftM toBitsFromMSB getTime
            atomically $ do
                sequence_
                    [ do
                        old <- readTVar var
                        when (old /= new) (writeTVar var new)
                    | (var, new) <- zip vars t ]
    return (TimeVar vars)

waitOnTimeVar :: TimeVar -> Time -> STM ()
waitOnTimeVar (TimeVar vars) t = do
    let tBits = toBitsFromMSB t
    cmp vars tBits
  where
    cmp (v:vs) (b:bs) = do
        vVal <- readTVar v
        case compare vVal b of
            LT -> retry
            EQ -> cmp vs bs
            GT -> return ()
    cmp [] [] = return ()

toBitsFromMSB :: Bits b => b -> [Bool]
toBitsFromMSB x = [ testBit x i | i <- [nBits-1, nBits-2 .. 0] ]
  where
    nBits = bitSize x

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to