Skip to content

Commit 8edf57f

Browse files
authored
Merge pull request SCons#4490 from mwichmann/maint/cachedir_lock
Lock creation of CacheDir config
2 parents f7e1135 + f13e0eb commit 8edf57f

File tree

5 files changed

+16
-10
lines changed

5 files changed

+16
-10
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
108108
DeprecatedMissingSConscriptWarning) add two warnings to manpage
109109
(cache-cleanup-error, future-reserved-variable), improve unittest, tweak
110110
Sphinx build.
111+
- Add locking around creation of CacheDir config file. Fixes #4489.
111112

112113

113114
RELEASE 4.6.0 - Sun, 19 Nov 2023 17:22:20 -0700

RELEASE.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ FIXES
6262
scanners - try harder to decode non-UTF-8 text.
6363
- PyPackageDir no longer fails if passed a module name which cannot be found,
6464
now returns None.
65+
- Add locking around creation of CacheDir config file. Fixes issue #4489.
6566

6667

6768
IMPROVEMENTS

SCons/CacheDir.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import SCons.Action
3535
import SCons.Errors
3636
import SCons.Warnings
37-
import SCons
37+
import SCons.Util
3838

3939
cache_enabled = True
4040
cache_debug = False
@@ -169,15 +169,16 @@ def _readconfig(self, path):
169169
"""
170170
config_file = os.path.join(path, 'config')
171171
try:
172+
# still use a try block even with exist_ok, might have other fails
172173
os.makedirs(path, exist_ok=True)
173-
except FileExistsError:
174-
pass
175174
except OSError:
176175
msg = "Failed to create cache directory " + path
177176
raise SCons.Errors.SConsEnvironmentError(msg)
178177

179178
try:
180-
with open(config_file, 'x') as config:
179+
with SCons.Util.FileLock(config_file, timeout=5, writer=True), open(
180+
config_file, "x"
181+
) as config:
181182
self.config['prefix_len'] = 2
182183
try:
183184
json.dump(self.config, config)
@@ -186,9 +187,11 @@ def _readconfig(self, path):
186187
raise SCons.Errors.SConsEnvironmentError(msg)
187188
except FileExistsError:
188189
try:
189-
with open(config_file) as config:
190+
with SCons.Util.FileLock(config_file, timeout=5, writer=False), open(
191+
config_file
192+
) as config:
190193
self.config = json.load(config)
191-
except ValueError:
194+
except (ValueError, json.decoder.JSONDecodeError):
192195
msg = "Failed to read cache configuration for " + path
193196
raise SCons.Errors.SConsEnvironmentError(msg)
194197

SCons/Tool/MSCommon/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def read_script_env_cache() -> dict:
173173
p = Path(CONFIG_CACHE)
174174
if not CONFIG_CACHE or not p.is_file():
175175
return envcache
176-
with SCons.Util.FileLock(CONFIG_CACHE, timeout=5, writer=True), p.open('r') as f:
176+
with SCons.Util.FileLock(CONFIG_CACHE, timeout=5, writer=False), p.open('r') as f:
177177
# Convert the list of cache entry dictionaries read from
178178
# json to the cache dictionary. Reconstruct the cache key
179179
# tuple from the key list written to json.

SCons/Util/filelock.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class SConsLockFailure(Exception):
4242
class FileLock:
4343
"""Lock a file using a lockfile.
4444
45-
Locking for the case where multiple processes try to hit an externally
45+
Basic locking for when multiple processes may hit an externally
4646
shared resource that cannot depend on locking within a single SCons
4747
process. SCons does not have a lot of those, but caches come to mind.
4848
@@ -51,8 +51,9 @@ class FileLock:
5151
and :meth:`release_lock`.
5252
5353
Lock can be a write lock, which is held until released, or a read
54-
lock, which releases immediately upon aquisition - the interesting
55-
thing being to not read a file which somebody else may be writing,
54+
lock, which releases immediately upon aquisition - we want to not
55+
read a file which somebody else may be writing, but not create the
56+
writers starvation problem of the classic readers/writers lock.
5657
5758
TODO: Should default timeout be None (non-blocking), or 0 (block forever),
5859
or some arbitrary number?

0 commit comments

Comments
 (0)