2 {-# LANGUAGE NamedFieldPuns #-}
3 {-# LANGUAGE RecordWildCards #-}
5 -- | Management for the installed package store.
6 module Distribution
.Client
.Store
7 ( -- * The store layout
9 , defaultStoreDirLayout
11 -- * Reading store entries
15 -- * Creating store entries
17 , NewStoreEntryOutcome
(..)
19 -- * Concurrency strategy
23 import Distribution
.Client
.Compat
.Prelude
26 import Distribution
.Client
.DistDirLayout
27 import Distribution
.Client
.RebuildMonad
29 import Distribution
.Compiler
(CompilerId
)
30 import Distribution
.Package
(UnitId
, mkUnitId
)
32 import Distribution
.Simple
.Utils
37 import Distribution
.Verbosity
41 import Control
.Exception
42 import qualified Data
.Set
as Set
43 import System
.Directory
44 import System
.FilePath
46 #ifdef MIN_VERSION_lukko
49 import System
.IO (openFile, IOMode(ReadWriteMode
), hClose)
50 import GHC
.IO.Handle.Lock
(hLock
, hTryLock
, LockMode
(ExclusiveLock
))
51 #if MIN_VERSION_base
(4,11,0)
52 import GHC
.IO.Handle.Lock
(hUnlock
)
58 -- We access and update the store concurrently. Our strategy to do that safely
61 -- The store entries once created are immutable. This alone simplifies matters
64 -- Additionally, the way 'UnitId' hashes are constructed means that if a store
65 -- entry exists already then we can assume its content is ok to reuse, rather
66 -- than having to re-recreate. This is the nix-style input hashing concept.
68 -- A consequence of this is that with a little care it is /safe/ to race
69 -- updates against each other. Consider two independent concurrent builds that
70 -- both want to build a particular 'UnitId', where that entry does not yet
71 -- exist in the store. It is safe for both to build and try to install this
72 -- entry into the store provided that:
74 -- * only one succeeds
75 -- * the looser discovers that they lost, they abandon their own build and
76 -- re-use the store entry installed by the winner.
78 -- Note that because builds are not reproducible in general (nor even
79 -- necessarily ABI compatible) then it is essential that the loser abandon
80 -- their build and use the one installed by the winner, so that subsequent
81 -- packages are built against the exact package from the store rather than some
82 -- morally equivalent package that may not be ABI compatible.
84 -- Our overriding goal is that store reads be simple, cheap and not require
85 -- locking. We will derive our write-side protocol to make this possible.
87 -- The read-side protocol is simply:
89 -- * check for the existence of a directory entry named after the 'UnitId' in
90 -- question. That is, if the dir entry @$root/foo-1.0-fe56a...@ exists then
91 -- the store entry can be assumed to be complete and immutable.
93 -- Given our read-side protocol, the final step on the write side must be to
94 -- atomically rename a fully-formed store entry directory into its final
95 -- location. While this will indeed be the final step, the preparatory steps
96 -- are more complicated. The tricky aspect is that the store also contains a
97 -- number of shared package databases (one per compiler version). Our read
98 -- strategy means that by the time we install the store dir entry the package
99 -- db must already have been updated. We cannot do the package db update
100 -- as part of atomically renaming the store entry directory however. Furthermore
101 -- it is not safe to allow either package db update because the db entry
102 -- contains the ABI hash and this is not guaranteed to be deterministic. So we
103 -- must register the new package prior to the atomic dir rename. Since this
104 -- combination of steps are not atomic then we need locking.
106 -- The write-side protocol is:
108 -- * Create a unique temp dir and write all store entry files into it.
110 -- * Take a lock named after the 'UnitId' in question.
112 -- * Once holding the lock, check again for the existence of the final store
113 -- entry directory. If the entry exists then the process lost the race and it
114 -- must abandon, unlock and re-use the existing store entry. If the entry
115 -- does not exist then the process won the race and it can proceed.
117 -- * Register the package into the package db. Note that the files are not in
118 -- their final location at this stage so registration file checks may need
121 -- * Atomically rename the temp dir to the final store entry location.
123 -- * Release the previously-acquired lock.
125 -- Obviously this means it is possible to fail after registering but before
126 -- installing the store entry, leaving a dangling package db entry. This is not
127 -- much of a problem because this entry does not determine package existence
128 -- for cabal. It does mean however that the package db update should be insert
129 -- or replace, i.e. not failing if the db entry already exists.
131 -- | Check if a particular 'UnitId' exists in the store.
132 doesStoreEntryExist
:: StoreDirLayout
-> CompilerId
-> UnitId
-> IO Bool
133 doesStoreEntryExist StoreDirLayout
{storePackageDirectory
} compid unitid
=
134 doesDirectoryExist (storePackageDirectory compid unitid
)
136 -- | Return the 'UnitId's of all packages\/components already installed in the
138 getStoreEntries
:: StoreDirLayout
-> CompilerId
-> Rebuild
(Set UnitId
)
139 getStoreEntries StoreDirLayout
{storeDirectory
} compid
= do
140 paths
<- getDirectoryContentsMonitored
(storeDirectory compid
)
141 return $! mkEntries paths
144 Set
.delete (mkUnitId
"package.db")
145 . Set
.delete (mkUnitId
"incoming")
149 valid
('.' : _
) = False
152 -- | The outcome of 'newStoreEntry': either the store entry was newly created
153 -- or it existed already. The latter case happens if there was a race between
154 -- two builds of the same store entry.
155 data NewStoreEntryOutcome
157 | UseExistingStoreEntry
160 -- | Place a new entry into the store. See the concurrency strategy description
163 -- In particular, it takes two actions: one to place files into a temporary
164 -- location, and a second to perform any necessary registration. The first
165 -- action is executed without any locks held (the temp dir is unique). The
166 -- second action holds a lock that guarantees that only one cabal process is
167 -- able to install this store entry. This means it is safe to register into
168 -- the compiler package DB or do other similar actions.
170 -- Note that if you need to use the registration information later then you
171 -- /must/ check the 'NewStoreEntryOutcome' and if it's'UseExistingStoreEntry'
172 -- then you must read the existing registration information (unless your
173 -- registration information is constructed fully deterministically).
179 -> (FilePath -> IO (FilePath, [FilePath]))
180 -- ^ Action to place files.
182 -- ^ Register action, if necessary.
183 -> IO NewStoreEntryOutcome
186 storeDirLayout
@StoreDirLayout
{..}
191 -- See $concurrency above for an explanation of the concurrency protocol
193 withTempIncomingDir storeDirLayout compid
$ \incomingTmpDir
-> do
194 -- Write all store entry files within the temp dir and return the prefix.
195 (incomingEntryDir
, otherFiles
) <- copyFiles incomingTmpDir
197 -- Take a lock named after the 'UnitId' in question.
198 withIncomingUnitIdLock verbosity storeDirLayout compid unitid
$ do
199 -- Check for the existence of the final store entry directory.
200 exists
<- doesStoreEntryExist storeDirLayout compid unitid
203 then -- If the entry exists then we lost the race and we must abandon,
204 -- unlock and re-use the existing store entry.
207 "Concurrent build race: abandoning build in favour of existing "
210 </> prettyShow unitid
211 return UseExistingStoreEntry
212 else -- If the entry does not exist then we won the race and can proceed.
214 -- Register the package into the package db (if appropriate).
217 -- Atomically rename the temp dir to the final store entry location.
218 renameDirectory incomingEntryDir finalEntryDir
219 for_ otherFiles
$ \file
-> do
220 let finalStoreFile
= storeDirectory compid
</> makeRelative
(incomingTmpDir
</> (dropDrive
(storeDirectory compid
))) file
221 createDirectoryIfMissing
True (takeDirectory finalStoreFile
)
222 renameFile file finalStoreFile
225 "Installed store entry " ++ prettyShow compid
</> prettyShow unitid
226 return UseNewStoreEntry
228 finalEntryDir
= storePackageDirectory compid unitid
233 -> (FilePath -> IO a
)
235 withTempIncomingDir StoreDirLayout
{storeIncomingDirectory
} compid action
= do
236 createDirectoryIfMissing
True incomingDir
237 withTempDirectory silent incomingDir
"new" action
239 incomingDir
= storeIncomingDirectory compid
241 withIncomingUnitIdLock
248 withIncomingUnitIdLock
250 StoreDirLayout
{storeIncomingLock
}
254 bracket takeLock releaseLock
(\_hnd
-> action
)
256 #ifdef MIN_VERSION_lukko
258 | fileLockingSupported
= do
259 fd
<- fdOpen
(storeIncomingLock compid unitid
)
260 gotLock
<- fdTryLock fd ExclusiveLock
262 info verbosity
$ "Waiting for file lock on store entry "
263 ++ prettyShow compid
</> prettyShow unitid
264 fdLock fd ExclusiveLock
267 -- if there's no locking, do nothing. Be careful on AIX.
268 |
otherwise = return undefined -- :(
271 | fileLockingSupported
= do
274 |
otherwise = return ()
277 h
<- openFile (storeIncomingLock compid unitid
) ReadWriteMode
278 -- First try non-blocking, but if we would have to wait then
279 -- log an explanation and do it again in blocking mode.
280 gotlock
<- hTryLock h ExclusiveLock
282 info verbosity
$ "Waiting for file lock on store entry "
283 ++ prettyShow compid
</> prettyShow unitid
284 hLock h ExclusiveLock
287 releaseLock h
= hUnlock h
>> hClose h