Skip to content

Commit ec437b7

Browse files
authored
add a suffix to a new cache files in case of failure of renaming it to an exisiting cache file already in use (#48137)
* add a suffix to a new cache files in case of failure of renaming it to an exisiting file
1 parent 45c81b1 commit ec437b7

File tree

2 files changed

+50
-4
lines changed

2 files changed

+50
-4
lines changed

base/loading.jl

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,8 @@ function find_all_in_cache_path(pkg::PkgId)
927927
end
928928

929929
ocachefile_from_cachefile(cachefile) = string(chopsuffix(cachefile, ".ji"), ".", Base.Libc.dlext)
930+
cachefile_from_ocachefile(cachefile) = string(chopsuffix(cachefile, ".$(Base.Libc.dlext)"), ".ji")
931+
930932

931933
# use an Int counter so that nested @time_imports calls all remain open
932934
const TIMING_IMPORTS = Threads.Atomic{Int}(0)
@@ -2091,7 +2093,6 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
20912093
if pkg.uuid !== nothing
20922094
entrypath, entryfile = cache_file_entry(pkg)
20932095
cachefiles = filter!(x -> startswith(x, entryfile * "_") && endswith(x, ".ji"), readdir(cachepath))
2094-
20952096
if length(cachefiles) >= MAX_NUM_PRECOMPILE_FILES[]
20962097
idx = findmin(mtime.(joinpath.(cachepath, cachefiles)))[2]
20972098
evicted_cachefile = joinpath(cachepath, cachefiles[idx])
@@ -2104,11 +2105,31 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
21042105
end
21052106
end
21062107

2107-
# this is atomic according to POSIX (not Win32):
2108-
rename(tmppath, cachefile; force=true)
21092108
if cache_objects
2110-
rename(tmppath_so, ocachefile::String; force=true)
2109+
try
2110+
rename(tmppath_so, ocachefile::String; force=true)
2111+
catch e
2112+
e isa IOError || rethrow()
2113+
isfile(ocachefile) || rethrow()
2114+
# Windows prevents renaming a file that is in use so if there is a Julia session started
2115+
# with a package image loaded, we cannot rename that file.
2116+
# The code belows append a `_i` to the name of the cache file where `i` is the smallest number such that
2117+
# that cache file does not exist.
2118+
ocachename, ocacheext = splitext(ocachefile)
2119+
old_cachefiles = Set(readdir(cachepath))
2120+
num = 1
2121+
while true
2122+
ocachefile = ocachename * "_$num" * ocacheext
2123+
in(basename(ocachefile), old_cachefiles) || break
2124+
num += 1
2125+
end
2126+
# TODO: Risk for a race here if some other process grabs this name before us
2127+
cachefile = cachefile_from_ocachefile(ocachefile)
2128+
rename(tmppath_so, ocachefile::String; force=true)
2129+
end
21112130
end
2131+
# this is atomic according to POSIX (not Win32):
2132+
rename(tmppath, cachefile; force=true)
21122133
return cachefile, ocachefile
21132134
end
21142135
finally

test/precompile.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,6 +1708,31 @@ precompile_test_harness("BadInvalidations") do load_path
17081708
end
17091709
end
17101710

1711+
# https://github.com/JuliaLang/julia/issues/48074
1712+
precompile_test_harness("WindowsCacheOverwrite") do load_path
1713+
# https://github.com/JuliaLang/julia/pull/47184#issuecomment-1364716312
1714+
write(joinpath(load_path, "WindowsCacheOverwrite.jl"),
1715+
"""
1716+
module WindowsCacheOverwrite
1717+
1718+
end # module
1719+
""")
1720+
ji, ofile = Base.compilecache(Base.PkgId("WindowsCacheOverwrite"))
1721+
(@eval (using WindowsCacheOverwrite))
1722+
1723+
write(joinpath(load_path, "WindowsCacheOverwrite.jl"),
1724+
"""
1725+
module WindowsCacheOverwrite
1726+
1727+
f() = "something new"
1728+
1729+
end # module
1730+
""")
1731+
1732+
ji_2, ofile_2 = Base.compilecache(Base.PkgId("WindowsCacheOverwrite"))
1733+
@test ofile_2 == Base.ocachefile_from_cachefile(ji_2)
1734+
end
1735+
17111736
empty!(Base.DEPOT_PATH)
17121737
append!(Base.DEPOT_PATH, original_depot_path)
17131738
empty!(Base.LOAD_PATH)

0 commit comments

Comments
 (0)