Skip to content

Commit

Permalink
add a suffix to a new cache files in case of failure of renaming it t…
Browse files Browse the repository at this point in the history
…o 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
  • Loading branch information
KristofferC authored Jan 9, 2023
1 parent 45c81b1 commit ec437b7
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 4 deletions.
29 changes: 25 additions & 4 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ function find_all_in_cache_path(pkg::PkgId)
end

ocachefile_from_cachefile(cachefile) = string(chopsuffix(cachefile, ".ji"), ".", Base.Libc.dlext)
cachefile_from_ocachefile(cachefile) = string(chopsuffix(cachefile, ".$(Base.Libc.dlext)"), ".ji")


# use an Int counter so that nested @time_imports calls all remain open
const TIMING_IMPORTS = Threads.Atomic{Int}(0)
Expand Down Expand Up @@ -2091,7 +2093,6 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
if pkg.uuid !== nothing
entrypath, entryfile = cache_file_entry(pkg)
cachefiles = filter!(x -> startswith(x, entryfile * "_") && endswith(x, ".ji"), readdir(cachepath))

if length(cachefiles) >= MAX_NUM_PRECOMPILE_FILES[]
idx = findmin(mtime.(joinpath.(cachepath, cachefiles)))[2]
evicted_cachefile = joinpath(cachepath, cachefiles[idx])
Expand All @@ -2104,11 +2105,31 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
end
end

# this is atomic according to POSIX (not Win32):
rename(tmppath, cachefile; force=true)
if cache_objects
rename(tmppath_so, ocachefile::String; force=true)
try
rename(tmppath_so, ocachefile::String; force=true)
catch e
e isa IOError || rethrow()
isfile(ocachefile) || rethrow()
# Windows prevents renaming a file that is in use so if there is a Julia session started
# with a package image loaded, we cannot rename that file.
# The code belows append a `_i` to the name of the cache file where `i` is the smallest number such that
# that cache file does not exist.
ocachename, ocacheext = splitext(ocachefile)
old_cachefiles = Set(readdir(cachepath))
num = 1
while true
ocachefile = ocachename * "_$num" * ocacheext
in(basename(ocachefile), old_cachefiles) || break
num += 1
end
# TODO: Risk for a race here if some other process grabs this name before us
cachefile = cachefile_from_ocachefile(ocachefile)
rename(tmppath_so, ocachefile::String; force=true)
end
end
# this is atomic according to POSIX (not Win32):
rename(tmppath, cachefile; force=true)
return cachefile, ocachefile
end
finally
Expand Down
25 changes: 25 additions & 0 deletions test/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,31 @@ precompile_test_harness("BadInvalidations") do load_path
end
end

# https://github.com/JuliaLang/julia/issues/48074
precompile_test_harness("WindowsCacheOverwrite") do load_path
# https://github.com/JuliaLang/julia/pull/47184#issuecomment-1364716312
write(joinpath(load_path, "WindowsCacheOverwrite.jl"),
"""
module WindowsCacheOverwrite
end # module
""")
ji, ofile = Base.compilecache(Base.PkgId("WindowsCacheOverwrite"))
(@eval (using WindowsCacheOverwrite))

write(joinpath(load_path, "WindowsCacheOverwrite.jl"),
"""
module WindowsCacheOverwrite
f() = "something new"
end # module
""")

ji_2, ofile_2 = Base.compilecache(Base.PkgId("WindowsCacheOverwrite"))
@test ofile_2 == Base.ocachefile_from_cachefile(ji_2)
end

empty!(Base.DEPOT_PATH)
append!(Base.DEPOT_PATH, original_depot_path)
empty!(Base.LOAD_PATH)
Expand Down

0 comments on commit ec437b7

Please sign in to comment.