@@ -49,6 +49,31 @@ import Distribution.Compat.GetShortPathName (getShortPathName)
4949import qualified Data.List.NonEmpty as NonEmpty
5050import qualified Data.Map as Map
5151
52+ import Control.Concurrent.MVar
53+ import Data.IORef
54+ import System.IO.Unsafe (unsafePerformIO )
55+ --- BEGIN MODIFICATION: Locking Mechanism ---
56+
57+ -- Global state for locks, one MVar per build directory path.
58+ -- The MVar () acts as a mutex: it's full when the lock is available (unlocked),
59+ -- and empty when taken (locked).
60+ {-# NOINLINE configureLocks #-}
61+ configureLocks :: IORef (Map. Map FilePath (MVar () ))
62+ configureLocks = unsafePerformIO (newIORef Map. empty)
63+
64+ -- Gets or creates a lock for a given path.
65+ -- Ensures that only one MVar is associated with each path.
66+ getOrCreateLock :: FilePath -> IO (MVar () )
67+ getOrCreateLock path = do
68+ -- Optimistically create a new MVar. It will be used if the path is not already in the map.
69+ -- This MVar is created "full" (i.e., containing ()), meaning the lock is initially available.
70+ newLock <- newMVar ()
71+ atomicModifyIORef' configureLocks $ \ currentMap ->
72+ case Map. lookup path currentMap of
73+ Just existingLock -> (currentMap, existingLock) -- Lock already exists, return it. The newLock we created is discarded.
74+ Nothing -> (Map. insert path newLock currentMap, newLock) -- New lock inserted, return it.
75+ --- END MODIFICATION: Locking Mechanism ---
76+
5277runConfigureScript
5378 :: ConfigFlags
5479 -> FlagAssignment
@@ -179,15 +204,32 @@ runConfigureScript cfg flags programDb hp = do
179204 shConfiguredProg <-
180205 lookupProgram shProg
181206 `fmap` configureProgram verbosity shProg progDb
182- case shConfiguredProg of
183- Just sh -> do
184- let build_in = interpretSymbolicPath mbWorkDir build_dir
185- createDirectoryIfMissing True build_in
186- runProgramInvocation verbosity $
187- (programInvocation (sh{programOverrideEnv = overEnv}) args')
188- { progInvokeCwd = Just build_in
189- }
190- Nothing -> dieWithException verbosity NotFoundMsg
207+
208+ --- BEGIN MODIFICATION: Apply Locking ---
209+ -- Acquire the lock specific to this build_dir
210+ -- build_dir is used as the key for the lock.
211+ configureLock <- getOrCreateLock (interpretSymbolicPath mbWorkDir build_dir)
212+
213+ let runLockedConfigureAction = do
214+ case shConfiguredProg of
215+ Just sh -> do
216+ let build_in = interpretSymbolicPath mbWorkDir build_dir
217+ createDirectoryIfMissing True build_in
218+ warn verbosity $ " Configure lock acquired. Running configure script in " ++ build_in
219+ runProgramInvocation verbosity $
220+ (programInvocation (sh{programOverrideEnv = overEnv}) args')
221+ { progInvokeCwd = Just build_in
222+ }
223+ Nothing -> dieWithException verbosity NotFoundMsg
224+
225+ warn verbosity $ " Attempting to acquire configure lock for " ++ interpretSymbolicPath mbWorkDir build_dir
226+ -- withMVar takes the MVar (blocks if already taken), runs the action,
227+ -- and ensures the MVar is put back, even if the action throws an exception.
228+ withMVar configureLock $ \ () ->
229+ -- The '()' means the MVar holds a unit value; we're interested in its full/empty state.
230+ runLockedConfigureAction
231+ warn verbosity $ " Configure lock released for " ++ interpretSymbolicPath mbWorkDir build_dir
232+ --- END MODIFICATION: Apply Locking ---
191233 where
192234 args = configureArgs backwardsCompatHack cfg
193235 backwardsCompatHack = False
0 commit comments