@@ -116,6 +116,14 @@ def normalizeBaseDir(baseDir):
116116 return None
117117
118118
119+ @contextlib .contextmanager
120+ def atomicWrite (fileName ):
121+ tempFileName = fileName + '.new'
122+ with open (tempFileName , 'w' ) as f :
123+ yield f
124+ os .replace (tempFileName , fileName )
125+
126+
119127class IncludeNotFoundException (Exception ):
120128 pass
121129
@@ -157,8 +165,9 @@ def addEntry(self, entry):
157165 """Adds entry at the top of the entries"""
158166 self ._entries .insert (0 , entry )
159167
160- def touchEntry (self , entryIndex ):
168+ def touchEntry (self , objectHash ):
161169 """Moves entry in entryIndex position to the top of entries()"""
170+ entryIndex = next ((i for i , e in enumerate (self .entries ()) if e .objectHash == objectHash ), 0 )
162171 self ._entries .insert (0 , self ._entries .pop (entryIndex ))
163172
164173
@@ -177,7 +186,7 @@ def setManifest(self, manifestHash, manifest):
177186 manifestPath = self .manifestPath (manifestHash )
178187 printTraceStatement ("Writing manifest with manifestHash = {} to {}" .format (manifestHash , manifestPath ))
179188 ensureDirectoryExists (self .manifestSectionDir )
180- with open (manifestPath , 'w' ) as outFile :
189+ with atomicWrite (manifestPath ) as outFile :
181190 # Converting namedtuple to JSON via OrderedDict preserves key names and keys order
182191 entries = [e ._asdict () for e in manifest .entries ()]
183192 jsonobject = {'entries' : entries }
@@ -637,7 +646,7 @@ def __init__(self, fileName):
637646
638647 def save (self ):
639648 if self ._dirty :
640- with open (self ._fileName , 'w' ) as f :
649+ with atomicWrite (self ._fileName ) as f :
641650 json .dump (self ._dict , f , sort_keys = True , indent = 4 )
642651
643652 def __setitem__ (self , key , value ):
@@ -967,7 +976,7 @@ def copyOrLink(srcFilePath, dstFilePath):
967976 # lower the chances of corrupting it.
968977 tempDst = dstFilePath + '.tmp'
969978 copyfile (srcFilePath , tempDst )
970- os .rename (tempDst , dstFilePath )
979+ os .replace (tempDst , dstFilePath )
971980
972981
973982def myExecutablePath ():
@@ -1392,17 +1401,17 @@ def printStatistics(cache):
13921401
13931402
13941403def resetStatistics (cache ):
1395- with cache .statistics as stats :
1404+ with cache .statistics . lock , cache . statistics as stats :
13961405 stats .resetCounters ()
13971406
13981407
13991408def cleanCache (cache ):
1400- with cache .statistics as stats , cache .configuration as cfg :
1409+ with cache .lock , cache . statistics as stats , cache .configuration as cfg :
14011410 cache .clean (stats , cfg .maximumCacheSize ())
14021411
14031412
14041413def clearCache (cache ):
1405- with cache .statistics as stats :
1414+ with cache .lock , cache . statistics as stats :
14061415 cache .clean (stats , 0 )
14071416
14081417
@@ -1508,25 +1517,21 @@ def main():
15081517 cache = Cache ()
15091518
15101519 if len (sys .argv ) == 2 and sys .argv [1 ] == "-s" :
1511- with cache .lock :
1512- printStatistics (cache )
1520+ printStatistics (cache )
15131521 return 0
15141522
15151523 if len (sys .argv ) == 2 and sys .argv [1 ] == "-c" :
1516- with cache .lock :
1517- cleanCache (cache )
1524+ cleanCache (cache )
15181525 print ('Cache cleaned' )
15191526 return 0
15201527
15211528 if len (sys .argv ) == 2 and sys .argv [1 ] == "-C" :
1522- with cache .lock :
1523- clearCache (cache )
1529+ clearCache (cache )
15241530 print ('Cache cleared' )
15251531 return 0
15261532
15271533 if len (sys .argv ) == 2 and sys .argv [1 ] == "-z" :
1528- with cache .lock :
1529- resetStatistics (cache )
1534+ resetStatistics (cache )
15301535 print ('Statistics reset' )
15311536 return 0
15321537
@@ -1638,8 +1643,7 @@ def scheduleJobs(cache, compiler, cmdLine, environment, sourceFiles, objectFiles
16381643 break
16391644
16401645 if cleanupRequired :
1641- with cache .lock :
1642- cleanCache (cache )
1646+ cleanCache (cache )
16431647
16441648 return exitCode
16451649
@@ -1661,33 +1665,35 @@ def processSingleSource(compiler, cmdLine, sourceFile, objectFile, environment):
16611665def processDirect (cache , objectFile , compiler , cmdLine , sourceFile ):
16621666 manifestHash = ManifestRepository .getManifestHash (compiler , cmdLine , sourceFile )
16631667 manifestHit = None
1664- with cache .manifestLockFor (manifestHash ):
1665- manifest = cache .getManifest (manifestHash )
1666- if manifest :
1667- for entryIndex , entry in enumerate (manifest .entries ()):
1668- # NOTE: command line options already included in hash for manifest name
1669- try :
1670- includesContentHash = ManifestRepository .getIncludesContentHashForFiles (
1671- [expandBasedirPlaceholder (path ) for path in entry .includeFiles ])
1668+ manifest = cache .getManifest (manifestHash )
1669+ if manifest :
1670+ for entryIndex , entry in enumerate (manifest .entries ()):
1671+ # NOTE: command line options already included in hash for manifest name
1672+ try :
1673+ includesContentHash = ManifestRepository .getIncludesContentHashForFiles (
1674+ [expandBasedirPlaceholder (path ) for path in entry .includeFiles ])
16721675
1673- if entry .includesContentHash == includesContentHash :
1674- cachekey = entry .objectHash
1675- assert cachekey is not None
1676+ if entry .includesContentHash == includesContentHash :
1677+ cachekey = entry .objectHash
1678+ assert cachekey is not None
1679+ if entryIndex > 0 :
16761680 # Move manifest entry to the top of the entries in the manifest
1677- manifest .touchEntry (entryIndex )
1678- cache .setManifest (manifestHash , manifest )
1681+ with cache .manifestLockFor (manifestHash ):
1682+ manifest = cache .getManifest (manifestHash ) or Manifest ()
1683+ manifest .touchEntry (cachekey )
1684+ cache .setManifest (manifestHash , manifest )
16791685
1680- manifestHit = True
1681- with cache .lockFor (cachekey ):
1682- if cache .hasEntry (cachekey ):
1683- return processCacheHit (cache , objectFile , cachekey )
1686+ manifestHit = True
1687+ with cache .lockFor (cachekey ):
1688+ if cache .hasEntry (cachekey ):
1689+ return processCacheHit (cache , objectFile , cachekey )
16841690
1685- except IncludeNotFoundException :
1686- pass
1691+ except IncludeNotFoundException :
1692+ pass
16871693
1688- unusableManifestMissReason = Statistics .registerHeaderChangedMiss
1689- else :
1690- unusableManifestMissReason = Statistics .registerSourceChangedMiss
1694+ unusableManifestMissReason = Statistics .registerHeaderChangedMiss
1695+ else :
1696+ unusableManifestMissReason = Statistics .registerSourceChangedMiss
16911697
16921698 if manifestHit is None :
16931699 stripIncludes = False
@@ -1700,22 +1706,21 @@ def processDirect(cache, objectFile, compiler, cmdLine, sourceFile):
17001706 includePaths , compilerOutput = parseIncludesSet (compilerResult [1 ], sourceFile , stripIncludes )
17011707 compilerResult = (compilerResult [0 ], compilerOutput , compilerResult [2 ])
17021708
1703- with cache .manifestLockFor (manifestHash ):
1704- if manifestHit is not None :
1705- return ensureArtifactsExist (cache , cachekey , unusableManifestMissReason ,
1706- objectFile , compilerResult )
1707-
1708- entry = createManifestEntry (manifestHash , includePaths )
1709- cachekey = entry .objectHash
1709+ if manifestHit is not None :
1710+ return ensureArtifactsExist (cache , cachekey , unusableManifestMissReason ,
1711+ objectFile , compilerResult )
17101712
1711- def addManifest ():
1713+ entry = createManifestEntry (manifestHash , includePaths )
1714+ cachekey = entry .objectHash
17121715
1716+ def addManifest ():
1717+ with cache .manifestLockFor (manifestHash ):
17131718 manifest = cache .getManifest (manifestHash ) or Manifest ()
17141719 manifest .addEntry (entry )
17151720 cache .setManifest (manifestHash , manifest )
17161721
1717- return ensureArtifactsExist (cache , cachekey , unusableManifestMissReason ,
1718- objectFile , compilerResult , addManifest )
1722+ return ensureArtifactsExist (cache , cachekey , unusableManifestMissReason ,
1723+ objectFile , compilerResult , addManifest )
17191724
17201725
17211726def processNoDirect (cache , objectFile , compiler , cmdLine , environment ):
0 commit comments