Ticket #3028: par1.hs

File par1.hs, 4.7 KB (added by osuch, 4 years ago)
Line 
1module Par1 (
2             Slot, 
3             CachedSlots, 
4             newCachedSlots,
5             getSlot,
6             waitSlot,
7             doubles,
8             numSlots,
9             numRandom,
10             startingGenerator,
11             startThread,
12             totalNeeded,
13            )
14where
15
16import Control.Concurrent
17import Control.Concurrent.STM
18import Control.Monad
19import Data.Array.Unboxed
20import System.Random
21import Data.IORef
22
23data Slot = Slot { doubles :: UArray Int Double }
24
25data CachedSlots = CachedSlots {
26  numSlots          :: Int,
27  numRandom         :: Int,
28  firstReady        :: TVar Int,
29  filledSlots       :: TVar Int,
30  slots             :: Array Int (TVar Slot),
31  order             :: TVar Int,
32  startingGenerator :: StdGen,
33  nextStart         :: MVar StdGen,
34  currentGenerator  :: IORef StdGen,
35  totalNeeded       :: TVar Int,
36  alreadyGenerated  :: IORef Int
37}
38
39-- Generates a given number of random doubles using provided IORef on generator
40--
41genArray :: (RandomGen t) => Int -> IORef t -> IO (UArray Int Double)
42genArray len generator = 
43               do 
44                  listValues <- forM [1..len] (\_ -> genAction)
45                  return (listArray (0, len - 1) listValues)
46               where 
47                  genAction ::  IO (Double)
48                  genAction = do
49                    g <- readIORef (generator)
50                    (number, g') <- return (randomR (0.0, 1.0) g)
51                    writeIORef generator g'
52                    return number
53                 
54genSlot numRandom currentRef n = do
55    arr <- genArray numRandom currentRef
56    return (Slot { doubles = arr})
57
58-- Creates an instance of CachedSlots
59--
60newCachedSlots :: Int -> Int -> StdGen -> IO CachedSlots
61newCachedSlots numSlots numRandom argGen = 
62  do
63    firstReady       <- newTVarIO (0 :: Int)
64    filledSlots      <- newTVarIO (numSlots)
65    currentRef       <- newIORef argGen
66    nextStart        <- newMVar argGen
67    listSlots        <- forM [1..numSlots] (genSlot numRandom currentRef)
68    listTSlots       <- mapM newTVarIO listSlots
69    order            <- newTVarIO (0)
70    totalNeeded      <- newTVarIO (0)
71    alreadyGenerated <- newIORef (numSlots)
72   
73    return (CachedSlots {
74      numSlots = numSlots,
75      numRandom = numRandom,
76      firstReady = firstReady,
77      filledSlots = filledSlots,
78     
79      slots = listArray (0, numSlots - 1) listTSlots,
80      order = order,
81      startingGenerator = argGen,
82      currentGenerator  = currentRef,
83      nextStart = nextStart,
84      totalNeeded = totalNeeded,
85      alreadyGenerated = alreadyGenerated
86      })
87
88
89startThread :: CachedSlots -> IO ThreadId
90startThread cs = forkIO (randomThreadS cs)
91 
92randomThreadS ::  CachedSlots -> IO ()
93randomThreadS ca = 
94  do 
95   takeMVar (nextStart ca) >>= writeIORef randomRef
96   readIORef (alreadyGenerated ca) >>= addSlots
97   readIORef randomRef >>= putMVar (nextStart ca) 
98    where
99      nSlots = numSlots ca
100      randomRef = currentGenerator ca
101      addSlots threadGenerated = 
102       do
103        precomputed <- genSlot (numRandom ca) randomRef threadGenerated
104        fContinue <- atomically (updateCA precomputed)
105        if fContinue
106          then addSlots (threadGenerated + 1) 
107          else do 
108            writeIORef (alreadyGenerated ca) threadGenerated
109            return ()
110       where 
111         updateCA precomputed = do
112           first   <- readTVar (firstReady ca)
113           nFilled <- readTVar (filledSlots ca)
114           needed  <- readTVar (totalNeeded ca)
115           if threadGenerated >= needed then return False
116             else if (nFilled == nSlots) 
117              then retry
118              else do 
119                    slotPosition <- return ((first + nFilled) `mod` nSlots)
120                    writeTVar ((slots ca)! slotPosition) precomputed
121                    writeTVar (filledSlots ca) (nFilled + 1)
122                    return True
123
124getSlot :: CachedSlots -> STM (Int, Slot)
125getSlot cs = do 
126    nFilled <- readTVar (filledSlots cs)
127    if nFilled == 0 then retry
128       else do 
129             first <- readTVar (firstReady cs)
130             nextFirst <- return ( (first + 1) `mod` (numSlots cs))
131             writeTVar (firstReady cs) nextFirst
132             slot <- readTVar ((slots cs)! first)
133             idx <- readTVar (order cs) 
134             writeTVar (order cs) (idx + 1)
135             writeTVar (filledSlots cs) (nFilled - 1)
136             return ((idx, slot))
137
138waitSlot :: CachedSlots -> Int -> STM (Int, Slot)
139waitSlot cs n = do
140       currentOrder <- readTVar (order cs)
141       if (currentOrder < n) 
142          then retry
143          else getSlot cs