Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

coot/timeout fix #81

Merged
merged 3 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 42 additions & 41 deletions io-sim/src/Control/Monad/IOSim/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,14 @@ schedule !thread@Thread{
locked <- readSTRef isLockedRef
case locked of
Locked etid -> do
let -- Kill the assassin throwing thread and carry on the
-- continuation
let -- Kill the assassin throwing thread then unmask exceptions and
-- carry on the continuation
thread' =
thread { threadControl =
ThreadControl (ThrowTo (toException ThreadKilled)
etid
(k (Just x)))
ctl'
(Return ()))
(MaskFrame (\_ -> k (Just x)) maskst ctl')
, threadMasking = MaskedUninterruptible
}
schedule thread' simstate
Expand Down Expand Up @@ -939,61 +939,63 @@ unblockThreads !wakeup !simstate@SimState {runqueue, threads} =
-- receive a 'ThreadKilled' exception.
--
forkTimeoutInterruptThreads :: forall s a.
[(ThreadId, TimeoutId, STRef s IsLocked, IsLocked)]
[(ThreadId, TimeoutId, STRef s IsLocked, IsLocked)]
-> SimState s a
-> ST s (SimState s a)
forkTimeoutInterruptThreads timeoutExpired simState@SimState {threads} =
forkTimeoutInterruptThreads timeoutExpired simState =
foldlM (\st@SimState{ runqueue = runqueue,
threads = threads'
}
(t0, t1, isLockedRef)
(t, isLockedRef)
-> do
let threads'' = Map.insert (threadId t0) t0
. Map.insert (threadId t1) t1
let threads'' = Map.insert (threadId t) t
$ threads'
runqueue' = Deque.snoc (threadId t1) runqueue
runqueue' = Deque.snoc (threadId t) runqueue

writeSTRef isLockedRef (Locked (threadId t1))
writeSTRef isLockedRef (Locked (threadId t))

return st { runqueue = runqueue',
threads = threads''
})
simState
simState'
throwToThread

where
-- can only throw exception if the thread exists and if the mutually
-- exclusive lock exists and is still 'NotLocked'
toThrow = [ (tid, tmid, ref, t)
| (tid, tmid, ref, locked) <- timeoutExpired
, Just t <- [Map.lookup tid threads]
, NotLocked <- [locked]
]
toThrow :: [(ThreadId, TimeoutId, STRef s IsLocked)]
toThrow = [ (tid, tmid, ref)
| (tid, tmid, ref, NotLocked) <- timeoutExpired ]

-- we launch a thread responsible for throwing an AsyncCancelled exception
-- to the thread which timeout expired
throwToThread :: [(Thread s a, Thread s a, STRef s IsLocked)]
throwToThread =
[ let nextId = threadNextTId t
tid' = childThreadId tid nextId
in ( t { threadNextTId = succ nextId }
, Thread { threadId = tid',
threadControl =
ThreadControl
(ThrowTo (toException (TimeoutException tmid))
tid
(Return ()))
ForkFrame,
threadBlocked = False,
threadMasking = Unmasked,
threadThrowTo = [],
threadClockId = threadClockId t,
threadLabel = Just "timeout-forked-thread",
threadNextTId = 1
}
, ref )
| (tid, tmid, ref, t) <- toThrow
]
throwToThread :: [(Thread s a, STRef s IsLocked)]

(simState', throwToThread) = List.mapAccumR fn simState toThrow
where
fn :: SimState s a
-> (ThreadId, TimeoutId, STRef s IsLocked)
-> (SimState s a, (Thread s a, STRef s IsLocked))
fn state@SimState { threads } (tid, tmid, ref) =
let t = threads Map.! tid
nextId = threadNextTId t
in ( state { threads = Map.insert tid t { threadNextTId = succ nextId } threads }
, ( Thread { threadId = childThreadId tid nextId,
threadControl =
ThreadControl
(ThrowTo (toException (TimeoutException tmid))
tid
(Return ()))
ForkFrame,
threadBlocked = False,
threadMasking = Unmasked,
threadThrowTo = [],
threadClockId = threadClockId t,
threadLabel = Just "timeout-forked-thread",
threadNextTId = 1
}
, ref )
)

-- | Iterate through the control stack to find an enclosing exception handler
-- of the right type, or unwind all the way to the top level for the thread.
Expand Down Expand Up @@ -1040,8 +1042,7 @@ unwindControlStack e thread =
unwind maskst (TimeoutFrame tmid _ k ctl) =
case fromException e of
-- Exception came from timeout expiring
Just (TimeoutException tmid') ->
assert (tmid == tmid')
Just (TimeoutException tmid') | tmid == tmid' ->
Right thread { threadControl = ThreadControl (k Nothing) ctl }
-- Exception came from a different exception
_ -> unwind maskst ctl
Expand Down
97 changes: 50 additions & 47 deletions io-sim/src/Control/Monad/IOSimPOR/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,14 @@ schedule thread@Thread{
locked <- readSTRef isLockedRef
case locked of
Locked etid -> do
let -- Kill the exception throwing thread and carry on the
-- continuation
let -- Kill the assassin throwing thread then unmask exceptions and
-- carry on the continuation
thread' =
thread { threadControl =
ThreadControl (ThrowTo (toException ThreadKilled)
etid
(k (Just x)))
ctl'
(Return ()))
(MaskFrame (\_ -> k (Just x)) maskst ctl')
, threadMasking = MaskedUninterruptible
}
schedule thread' simstate
Expand Down Expand Up @@ -1165,62 +1165,66 @@ forkTimeoutInterruptThreads :: forall s a.
[(ThreadId, TimeoutId, STRef s IsLocked, IsLocked)]
-> SimState s a
-> ST s (SimState s a)
forkTimeoutInterruptThreads timeoutExpired simState@SimState {threads} =
forkTimeoutInterruptThreads timeoutExpired simState =
foldlM (\st@SimState{ runqueue = runqueue,
threads = threads'
}
(t0, t1, isLockedRef)
(t, isLockedRef)
-> do
let threads'' = Map.insert (threadId t0) t0
. Map.insert (threadId t1) t1
let threads'' = Map.insert (threadId t) t
$ threads'
runqueue' = insertThread t1 runqueue
writeSTRef isLockedRef (Locked (threadId t1))
runqueue' = insertThread t runqueue
writeSTRef isLockedRef (Locked (threadId t))

return st { runqueue = runqueue',
threads = threads''
})
simState
simState'
throwToThread

where
-- can only throw exception if the thread exists and if the mutually
-- exclusive lock exists and is still 'NotLocked'
toThrow = [ (tid, tmid, ref, t)
| (tid, tmid, ref, locked) <- timeoutExpired
, Just t <- [Map.lookup tid threads]
, NotLocked <- [locked]
]
toThrow :: [(ThreadId, TimeoutId, STRef s IsLocked)]
toThrow = [ (tid, tmid, ref)
| (tid, tmid, ref, NotLocked) <- timeoutExpired ]

-- we launch a thread responsible for throwing an AsyncCancelled exception
-- to the thread which timeout expired
throwToThread :: [(Thread s a, Thread s a, STRef s IsLocked)]
throwToThread =
[ let nextId = threadNextTId t
tid' = childThreadId tid nextId
in ( t { threadNextTId = nextId + 1 }
, Thread { threadId = tid',
threadControl =
ThreadControl
(ThrowTo (toException (TimeoutException tmid))
tid
(Return ()))
ForkFrame,
threadBlocked = False,
threadDone = False,
threadMasking = Unmasked,
threadThrowTo = [],
threadClockId = threadClockId t,
threadLabel = Just "timeout-forked-thread",
threadNextTId = 1,
threadStep = 0,
threadVClock = insertVClock tid' 0
$ threadVClock t,
threadEffect = mempty,
threadRacy = threadRacy t
}
, ref)
| (tid, tmid, ref, t) <- toThrow
]
throwToThread :: [(Thread s a, STRef s IsLocked)]
(simState', throwToThread) = List.mapAccumR fn simState toThrow
where
fn :: SimState s a
-> (ThreadId, TimeoutId, STRef s IsLocked)
-> (SimState s a, (Thread s a, STRef s IsLocked))
fn state@SimState { threads } (tid, tmid, ref) =
let t = threads Map.! tid
nextId = threadNextTId t
tid' = childThreadId tid nextId
in ( state { threads = Map.insert tid t { threadNextTId = succ nextId } threads }
, ( Thread { threadId = tid',
threadControl =
ThreadControl
(ThrowTo (toException (TimeoutException tmid))
tid
(Return ()))
ForkFrame,
threadBlocked = False,
threadDone = False,
threadMasking = Unmasked,
threadThrowTo = [],
threadClockId = threadClockId t,
threadLabel = Just "timeout-forked-thread",
threadNextTId = 1,
threadStep = 0,
threadVClock = insertVClock tid' 0
$ threadVClock t,
threadEffect = mempty,
threadRacy = threadRacy t
}
, ref )
)


-- | Iterate through the control stack to find an enclosing exception handler
-- of the right type, or unwind all the way to the top level for the thread.
Expand Down Expand Up @@ -1267,10 +1271,9 @@ unwindControlStack e thread =
unwind maskst (TimeoutFrame tmid isLockedRef k ctl) =
case fromException e of
-- Exception came from timeout expiring
Just (TimeoutException tmid') ->
assert (tmid == tmid')
Just (TimeoutException tmid') | tmid == tmid' ->
Right thread { threadControl = ThreadControl (k Nothing) ctl }
-- Exception came from a different exception
-- Exception came from a different exception
_ -> unwind maskst ctl

atLeastInterruptibleMask :: MaskingState -> MaskingState
Expand Down
Loading