🤖 task 3 and 4
This commit is contained in:
@ -18,6 +18,9 @@ module SPC
|
|||||||
WorkerName,
|
WorkerName,
|
||||||
workerAdd,
|
workerAdd,
|
||||||
workerStop,
|
workerStop,
|
||||||
|
|
||||||
|
-- debugState,
|
||||||
|
-- SPCState (..)
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -26,9 +29,10 @@ import Control.Concurrent
|
|||||||
killThread,
|
killThread,
|
||||||
threadDelay,
|
threadDelay,
|
||||||
newChan,
|
newChan,
|
||||||
readChan
|
readChan,
|
||||||
|
ThreadId
|
||||||
)
|
)
|
||||||
import Control.Monad (ap, forever, liftM, void, filterM)
|
import Control.Monad (ap, forever, liftM, void, filterM, when)
|
||||||
import GenServer
|
import GenServer
|
||||||
import System.Clock.Seconds (Clock (Monotonic), Seconds, getTime)
|
import System.Clock.Seconds (Clock (Monotonic), Seconds, getTime)
|
||||||
import GHC.RTS.Flags (DebugFlags(scheduler))
|
import GHC.RTS.Flags (DebugFlags(scheduler))
|
||||||
@ -97,7 +101,7 @@ type WorkerName = String
|
|||||||
-- processes spawned by the workes.
|
-- processes spawned by the workes.
|
||||||
data WorkerMsg
|
data WorkerMsg
|
||||||
= -- | New job time
|
= -- | New job time
|
||||||
MsgStartJob (IO ()) JobId
|
MsgStartJob (IO ()) JobId (ReplyChan ThreadId)
|
||||||
|
|
||||||
-- Messages sent to SPC.
|
-- Messages sent to SPC.
|
||||||
data SPCMsg
|
data SPCMsg
|
||||||
@ -117,6 +121,7 @@ data SPCMsg
|
|||||||
MsgAddWorker WorkerName Worker
|
MsgAddWorker WorkerName Worker
|
||||||
| -- | Worker finished job
|
| -- | Worker finished job
|
||||||
MsgJobDone JobId
|
MsgJobDone JobId
|
||||||
|
-- | MsgDebug (ReplyChan SPCState)
|
||||||
|
|
||||||
-- | A handle to the SPC instance.
|
-- | A handle to the SPC instance.
|
||||||
data SPC = SPC (Server SPCMsg)
|
data SPC = SPC (Server SPCMsg)
|
||||||
@ -127,7 +132,7 @@ data Worker = Worker (Server WorkerMsg)
|
|||||||
-- | The central state. Must be protected from the bourgeoisie.
|
-- | The central state. Must be protected from the bourgeoisie.
|
||||||
data SPCState = SPCState
|
data SPCState = SPCState
|
||||||
{ spcJobsPending :: [(JobId, Job)],
|
{ spcJobsPending :: [(JobId, Job)],
|
||||||
spcJobsRunning :: [(JobId, (Job, WorkerName))],
|
spcJobsRunning :: [(JobId, (WorkerName, Seconds, ThreadId))],
|
||||||
spcJobsDone :: [(JobId, JobDoneReason)],
|
spcJobsDone :: [(JobId, JobDoneReason)],
|
||||||
spcJobCounter :: JobId,
|
spcJobCounter :: JobId,
|
||||||
spcWorkers :: [(WorkerName, Worker)],
|
spcWorkers :: [(WorkerName, Worker)],
|
||||||
@ -179,13 +184,19 @@ runSPCM state (SPCM f) = fst <$> f state
|
|||||||
workerIsIdle :: (WorkerName, Worker) -> SPCM Bool
|
workerIsIdle :: (WorkerName, Worker) -> SPCM Bool
|
||||||
workerIsIdle (name, _) = do
|
workerIsIdle (name, _) = do
|
||||||
state <- get
|
state <- get
|
||||||
pure (all (\(_, (_,w)) -> w /= name) (spcJobsRunning state))
|
pure (all (\(_, (w,_,_)) -> w /= name) (spcJobsRunning state))
|
||||||
|
|
||||||
workerIsGone :: WorkerName -> SPCM ()
|
checkJobTimeout :: (JobId, (WorkerName, Seconds, ThreadId)) -> SPCM ()
|
||||||
workerIsGone = undefined
|
checkJobTimeout (jobid, (_, deadline, t)) = do
|
||||||
|
now <- io $ getSeconds
|
||||||
|
when (now >= deadline) $ do
|
||||||
|
io $ killThread t
|
||||||
|
jobDone jobid DoneTimeout
|
||||||
|
|
||||||
checkTimeouts :: SPCM ()
|
checkTimeouts :: SPCM ()
|
||||||
checkTimeouts = pure () -- change in Task 4
|
checkTimeouts = do
|
||||||
|
state <- get
|
||||||
|
mapM_ checkJobTimeout (spcJobsRunning state)
|
||||||
|
|
||||||
getIdleWorkers :: SPCM [(WorkerName, Worker)]
|
getIdleWorkers :: SPCM [(WorkerName, Worker)]
|
||||||
getIdleWorkers = do
|
getIdleWorkers = do
|
||||||
@ -201,10 +212,12 @@ schedule = do
|
|||||||
case workers of
|
case workers of
|
||||||
(workerName,worker):_ -> do
|
(workerName,worker):_ -> do
|
||||||
w <- (\(Worker w) -> pure w) worker
|
w <- (\(Worker w) -> pure w) worker
|
||||||
io $ sendTo w (MsgStartJob (jobAction job) jobid)
|
threadId <- io $ requestReply w (MsgStartJob (jobAction job) jobid)
|
||||||
|
now <- io $ getSeconds
|
||||||
|
let deadline = now + fromIntegral (jobMaxSeconds job)
|
||||||
put $
|
put $
|
||||||
state
|
state
|
||||||
{ spcJobsRunning = (jobid, (job, workerName)) : spcJobsRunning state,
|
{ spcJobsRunning = (jobid, (workerName, deadline, threadId)) : spcJobsRunning state,
|
||||||
spcJobsPending = jobs
|
spcJobsPending = jobs
|
||||||
}
|
}
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
@ -251,7 +264,7 @@ handleMsg c = do
|
|||||||
MsgJobDone jobid -> do
|
MsgJobDone jobid -> do
|
||||||
state <- get
|
state <- get
|
||||||
case (lookup jobid $ spcJobsRunning state) of
|
case (lookup jobid $ spcJobsRunning state) of
|
||||||
Just (job, _) -> do
|
Just (_, _, _) -> do
|
||||||
jobDone jobid Done
|
jobDone jobid Done
|
||||||
Nothing -> pure ()
|
Nothing -> pure ()
|
||||||
MsgJobWait jobid rsvp -> do
|
MsgJobWait jobid rsvp -> do
|
||||||
@ -261,14 +274,21 @@ handleMsg c = do
|
|||||||
io $ reply rsvp $ reason
|
io $ reply rsvp $ reason
|
||||||
Nothing ->
|
Nothing ->
|
||||||
put $ state {spcWaiting = (jobid, rsvp) : spcWaiting state}
|
put $ state {spcWaiting = (jobid, rsvp) : spcWaiting state}
|
||||||
|
MsgJobCancel jobid -> do
|
||||||
|
state <- get
|
||||||
|
case (lookup jobid $ spcJobsRunning state, lookup jobid $ spcJobsPending state) of
|
||||||
|
(Just (_,_,t), _) -> do
|
||||||
|
io $ killThread t
|
||||||
|
jobDone jobid DoneCancelled
|
||||||
|
(_, Just _) -> do
|
||||||
|
put $
|
||||||
|
state
|
||||||
|
{ spcJobsPending = removeAssoc jobid $ spcJobsPending state,
|
||||||
|
spcJobsDone = (jobid, DoneCancelled) : spcJobsDone state
|
||||||
|
}
|
||||||
|
_ -> pure ()
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
deleteJob :: JobId -> [(JobId, (Job, WorkerName))] -> [(JobId, (Job, WorkerName))]
|
|
||||||
deleteJob jobid list =
|
|
||||||
case list of
|
|
||||||
[] -> []
|
|
||||||
(jid, (job, w)):l -> if (jid == jobid) then l else (jid,(job,w)):(deleteJob jobid l)
|
|
||||||
|
|
||||||
startSPC :: IO SPC
|
startSPC :: IO SPC
|
||||||
startSPC = do
|
startSPC = do
|
||||||
let initial_state =
|
let initial_state =
|
||||||
@ -302,7 +322,7 @@ jobDone jobid reason = do
|
|||||||
put $
|
put $
|
||||||
state
|
state
|
||||||
{ spcJobsRunning =
|
{ spcJobsRunning =
|
||||||
deleteJob jobid (spcJobsRunning state),
|
removeAssoc jobid $ spcJobsRunning state,
|
||||||
spcJobsDone =
|
spcJobsDone =
|
||||||
(jobid, reason) : spcJobsDone state
|
(jobid, reason) : spcJobsDone state
|
||||||
}
|
}
|
||||||
@ -327,6 +347,10 @@ jobCancel :: SPC -> JobId -> IO ()
|
|||||||
jobCancel (SPC c) jobid =
|
jobCancel (SPC c) jobid =
|
||||||
sendTo c $ MsgJobCancel jobid
|
sendTo c $ MsgJobCancel jobid
|
||||||
|
|
||||||
|
-- debugState :: SPC -> IO SPCState
|
||||||
|
-- debugState (SPC c) =
|
||||||
|
-- requestReply c $ MsgDebug
|
||||||
|
|
||||||
-- | Add a new worker with this name. Fails with 'Left' if a worker
|
-- | Add a new worker with this name. Fails with 'Left' if a worker
|
||||||
-- with that name already exists.
|
-- with that name already exists.
|
||||||
workerAdd :: SPC -> WorkerName -> IO (Either String Worker)
|
workerAdd :: SPC -> WorkerName -> IO (Either String Worker)
|
||||||
@ -335,23 +359,25 @@ workerAdd (SPC c) name = do
|
|||||||
if exists
|
if exists
|
||||||
then pure $ Left "Worker with given name already exist"
|
then pure $ Left "Worker with given name already exist"
|
||||||
else do
|
else do
|
||||||
worker <- workerSpawn name c
|
worker <- workerSpawn c
|
||||||
sendTo c $ MsgAddWorker name worker
|
sendTo c $ MsgAddWorker name worker
|
||||||
pure $ Right worker
|
pure $ Right worker
|
||||||
|
|
||||||
workerSpawn :: WorkerName -> (Server SPCMsg) -> IO Worker
|
workerSpawn :: (Server SPCMsg) -> IO Worker
|
||||||
workerSpawn name c = do
|
workerSpawn c = do
|
||||||
w <- spawn $ workerLoop name c
|
w <- spawn $ workerLoop c
|
||||||
pure $ Worker w
|
pure $ Worker w
|
||||||
|
|
||||||
workerLoop :: WorkerName -> (Server SPCMsg) -> Chan WorkerMsg -> IO ()
|
workerLoop :: (Server SPCMsg) -> Chan WorkerMsg -> IO ()
|
||||||
workerLoop name c m = do
|
workerLoop c m = forever $ do
|
||||||
msg <- receive m
|
msg <- receive m
|
||||||
case msg of
|
case msg of
|
||||||
-- stuff happening here
|
-- stuff happening here
|
||||||
MsgStartJob action jobid -> do
|
MsgStartJob action jobid rsvp -> do
|
||||||
action
|
t <- forkIO $ do
|
||||||
sendTo c $ MsgJobDone jobid
|
action
|
||||||
|
sendTo c $ MsgJobDone jobid
|
||||||
|
reply rsvp t
|
||||||
|
|
||||||
-- | Shut down a running worker. No effect if the worker is already
|
-- | Shut down a running worker. No effect if the worker is already
|
||||||
-- terminated.
|
-- terminated.
|
||||||
|
@ -17,36 +17,118 @@ tests =
|
|||||||
[
|
[
|
||||||
testCase "workerAdd" $ do
|
testCase "workerAdd" $ do
|
||||||
spc <- startSPC
|
spc <- startSPC
|
||||||
|
|
||||||
w <- workerAdd spc "R2-D2"
|
w <- workerAdd spc "R2-D2"
|
||||||
isRight w @?= True,
|
isRight w @?= True,
|
||||||
testCase "workerAdd (2)" $ do
|
testCase "workerAdd (2)" $ do
|
||||||
spc <- startSPC
|
spc <- startSPC
|
||||||
|
|
||||||
w1 <- workerAdd spc "MSE-6"
|
w1 <- workerAdd spc "MSE-6"
|
||||||
isRight w1 @?= True
|
isRight w1 @?= True
|
||||||
|
|
||||||
w2 <- workerAdd spc "GNK"
|
w2 <- workerAdd spc "GNK"
|
||||||
isRight w2 @?= True,
|
isRight w2 @?= True,
|
||||||
testCase "workerAdd (3)" $ do
|
|
||||||
spc <- startSPC
|
|
||||||
w1 <- workerAdd spc "C-3PO"
|
|
||||||
isRight w1 @?= True
|
|
||||||
w2 <- workerAdd spc "K-2SO"
|
|
||||||
isRight w2 @?= True
|
|
||||||
w3 <- workerAdd spc "IG-88"
|
|
||||||
isRight w3 @?= True,
|
|
||||||
testCase "workerAdd (fail)" $ do
|
testCase "workerAdd (fail)" $ do
|
||||||
spc <- startSPC
|
spc <- startSPC
|
||||||
|
|
||||||
w1 <- workerAdd spc "BD-1"
|
w1 <- workerAdd spc "BD-1"
|
||||||
isRight w1 @?= True
|
isRight w1 @?= True
|
||||||
|
|
||||||
w2 <- workerAdd spc "BD-1"
|
w2 <- workerAdd spc "BD-1"
|
||||||
isRight w2 @?= False,
|
isRight w2 @?= False,
|
||||||
testCase "Running a job" $ do
|
testCase "Running a job" $ do
|
||||||
ref <- newIORef False
|
ref <- newIORef False
|
||||||
spc <- startSPC
|
spc <- startSPC
|
||||||
|
|
||||||
w <- workerAdd spc "R5-D4"
|
w <- workerAdd spc "R5-D4"
|
||||||
isRight w @?= True
|
isRight w @?= True
|
||||||
|
|
||||||
j <- jobAdd spc $ Job (writeIORef ref True) 1
|
j <- jobAdd spc $ Job (writeIORef ref True) 1
|
||||||
r <- jobWait spc j
|
r <- jobWait spc j
|
||||||
r @?= Done
|
r @?= Done
|
||||||
|
|
||||||
|
x <- readIORef ref
|
||||||
|
x @?= True,
|
||||||
|
testCase "Running two jobs" $ do
|
||||||
|
ref <- newIORef 0
|
||||||
|
spc <- startSPC
|
||||||
|
|
||||||
|
w <- workerAdd spc "K-2SO"
|
||||||
|
isRight w @?= True
|
||||||
|
|
||||||
|
j1 <- jobAdd spc $ Job (writeIORef ref 1) 1
|
||||||
|
r1 <- jobWait spc j1
|
||||||
|
r1 @?= Done
|
||||||
|
|
||||||
|
x1 <- readIORef ref
|
||||||
|
x1 @?= 1
|
||||||
|
|
||||||
|
j2 <- jobAdd spc $ Job (writeIORef ref 2) 1
|
||||||
|
r2 <- jobWait spc j2
|
||||||
|
r2 @?= Done
|
||||||
|
|
||||||
|
x2 <- readIORef ref
|
||||||
|
x2 @?= 2,
|
||||||
|
testCase "Canceling job (pending)" $ do
|
||||||
|
spc <- startSPC
|
||||||
|
j <- jobAdd spc $ Job (pure ()) 1
|
||||||
|
jobCancel spc j
|
||||||
|
r <- jobStatus spc j
|
||||||
|
r @?= JobDone DoneCancelled,
|
||||||
|
testCase "Canceling job (running)" $ do
|
||||||
|
spc <- startSPC
|
||||||
|
|
||||||
|
w <- workerAdd spc "IG-88"
|
||||||
|
isRight w @?= True
|
||||||
|
|
||||||
|
j <- jobAdd spc $ Job (threadDelay 2000000) 2
|
||||||
|
jobCancel spc j
|
||||||
|
r <- jobStatus spc j
|
||||||
|
r @?= JobDone DoneCancelled,
|
||||||
|
testCase "Canceling job (running) (new job)" $ do
|
||||||
|
ref <- newIORef False
|
||||||
|
spc <- startSPC
|
||||||
|
|
||||||
|
w <- workerAdd spc "C-3PO"
|
||||||
|
isRight w @?= True
|
||||||
|
|
||||||
|
j1 <- jobAdd spc $ Job (threadDelay 2000000) 2
|
||||||
|
jobCancel spc j1
|
||||||
|
r1 <- jobStatus spc j1
|
||||||
|
r1 @?= JobDone DoneCancelled
|
||||||
|
|
||||||
|
-- job has been cancelled. Starting new job
|
||||||
|
j2 <- jobAdd spc $ Job (writeIORef ref True) 1
|
||||||
|
r2 <- jobWait spc j2
|
||||||
|
r2 @?= Done
|
||||||
|
|
||||||
|
x <- readIORef ref
|
||||||
|
x @?= True,
|
||||||
|
testCase "Timeout" $ do
|
||||||
|
spc <- startSPC
|
||||||
|
|
||||||
|
w <- workerAdd spc "L3-37"
|
||||||
|
isRight w @?= True
|
||||||
|
|
||||||
|
j <- jobAdd spc $ Job (threadDelay 2000000) 1
|
||||||
|
r <- jobWait spc j
|
||||||
|
|
||||||
|
r @?= DoneTimeout,
|
||||||
|
testCase "Timeout (2 jobs)" $ do
|
||||||
|
ref <- newIORef False
|
||||||
|
spc <- startSPC
|
||||||
|
|
||||||
|
w <- workerAdd spc "General Kalani"
|
||||||
|
isRight w @?= True
|
||||||
|
|
||||||
|
j1 <- jobAdd spc $ Job (threadDelay 2000000) 1
|
||||||
|
j2 <- jobAdd spc $ Job (writeIORef ref True) 1
|
||||||
|
r1 <- jobWait spc j1
|
||||||
|
r1 @?= DoneTimeout
|
||||||
|
|
||||||
|
r2 <- jobWait spc j2
|
||||||
|
r2 @?= Done
|
||||||
|
|
||||||
x <- readIORef ref
|
x <- readIORef ref
|
||||||
x @?= True
|
x @?= True
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user