@@ -49,6 +49,31 @@ import Distribution.Compat.GetShortPathName (getShortPathName)
49
49
import qualified Data.List.NonEmpty as NonEmpty
50
50
import qualified Data.Map as Map
51
51
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
+
52
77
runConfigureScript
53
78
:: ConfigFlags
54
79
-> FlagAssignment
@@ -179,15 +204,32 @@ runConfigureScript cfg flags programDb hp = do
179
204
shConfiguredProg <-
180
205
lookupProgram shProg
181
206
`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 ---
191
233
where
192
234
args = configureArgs backwardsCompatHack cfg
193
235
backwardsCompatHack = False
0 commit comments