Fix a crash (STM deadlock) when -J is used with multiple files that point to the same key

See the comment for a trace of the deadlock.

Added a new StartStage. New worker threads begin in the StartStage.
Once a thread is ready to do work, it moves away from the StartStage,
and no thread will ever transition back to it.

A thread that blocks waiting on another thread that is processing
the same key will block while in the StartStage. That other thread
will never switch back to the StartStage, and so the deadlock is avoided.
This commit is contained in:
Joey Hess 2019-11-14 11:31:43 -04:00
parent 20d9a9b662
commit 667d38a8f1
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
6 changed files with 114 additions and 24 deletions

View file

@ -40,7 +40,12 @@ instance Show (Worker t) where
show (ActiveWorker _ s) = "ActiveWorker " ++ show s
data WorkerStage
= PerformStage
= StartStage
-- ^ All threads start in this stage, and then transition away from
-- it to the initialStage when they begin doing work. This should
-- never be included in UsedStages, because transition from some
-- other stage back to this one could result in a deadlock.
| PerformStage
-- ^ Running a CommandPerform action.
| CleanupStage
-- ^ Running a CommandCleanup action.
@ -102,12 +107,13 @@ workerAsync (ActiveWorker aid _) = Just aid
allocateWorkerPool :: t -> Int -> UsedStages -> WorkerPool t
allocateWorkerPool t n u = WorkerPool
{ usedStages = u
, workerList = take totalthreads $ map IdleWorker stages
, workerList = map IdleWorker $
take totalthreads $ concat $ repeat stages
, spareVals = replicate totalthreads t
}
where
stages = concat $ repeat $ S.toList $ stageSet u
totalthreads = n * S.size (stageSet u)
stages = StartStage : S.toList (stageSet u)
totalthreads = n * length stages
addWorkerPool :: Worker t -> WorkerPool t -> WorkerPool t
addWorkerPool w pool = pool { workerList = w : workerList pool }