From 990be4ce6891e919a5264f984b7c5b7c87225a13 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 16 Oct 2022 16:58:53 +0200 Subject: [PATCH] Rewrite force_symlink to avoid a race condition --- src/setup.jl | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/setup.jl b/src/setup.jl index f52c33cb..00c2b0b7 100644 --- a/src/setup.jl +++ b/src/setup.jl @@ -23,9 +23,21 @@ gaproot() = @get_scratch!(scratch_key) # ############################################################################# -function force_symlink(p::AbstractString, np::AbstractString) - rm(np; force = true) - symlink(p, np) +# ensure `source` is a symlink pointing to `target` in a way that is hopefully +# safe against races with other Julia processes doing the exact same thing +function force_symlink(source::AbstractString, target::AbstractString) + # We previously used `rm` followed by `symlink`, but this can cause a + # race if multiple processes invoke `rm` concurrently (which works if + # one uses `force=true`), and then try to invoke `symlink` + # concurrently (which then fails in all but one process). + # + # So instead we create the symlink with a temporary name, and then use + # an atomic `rename` to rename it to the `target` name. The latter + # unfortunately requires invoking an undocumented function. + tmpfile = tempname(dirname(abspath(target)); cleanup=false) + symlink(source, tmpfile) + Base.Filesystem.rename(tmpfile, target) + return nothing end function read_sysinfo_gap(dir::String)