From fdfbf89f0c650a9886ffa91e4f3fda08080702b9 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Thu, 4 Mar 2021 09:06:34 -0500
Subject: ExternalData: Avoid replacing a concurrently-created object

If more than one content link references the same object, the build
system may launch multiple download processes for the same object
concurrently.  Use whichever one finishes first, and discard the others.

Without this, we replace the objects and use the last finisher instead
of the first.  This is okay on non-Windows platforms where `rename(2)`
gives reliable atomic replacement.  However, on Windows platforms and
NTFS this is less reliable.  I've observed `MoveFileEx` somehow cause
another process to get `ERROR_SHARING_VIOLATION` when attempting to read
the destination file.  We may be able to improve the `file(RENAME)`
implementation on modern Windows 10 versions, but for ExternalData's use
case it is simpler to just not replace existing objects.
---
 Modules/ExternalData.cmake | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/Modules/ExternalData.cmake b/Modules/ExternalData.cmake
index d6fbae9..b439636 100644
--- a/Modules/ExternalData.cmake
+++ b/Modules/ExternalData.cmake
@@ -1101,7 +1101,14 @@ function(_ExternalData_download_object name hash algo var_obj var_success var_er
 
   set(success 1)
   if(found)
-    file(RENAME "${tmp}" "${obj}")
+    # Atomically create the object.  If we lose a race with another process,
+    # do not replace it.  Content-addressing ensures it has what we expect.
+    file(RENAME "${tmp}" "${obj}" NO_REPLACE RESULT result)
+    if (result STREQUAL "NO_REPLACE")
+      file(REMOVE "${tmp}")
+    elseif (result)
+      message(FATAL_ERROR "Failed to rename:\n  \"${tmp}\"\nto:\n  \"${obj}\"\nwith error:\n  ${result}")
+    endif()
     message(STATUS "Downloaded object: \"${obj}\"")
   elseif(EXISTS "${staged}")
     set(obj "${staged}")
-- 
cgit v0.12


From 31be23dd1d61db80656159adbcf915a609b8692f Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Fri, 5 Mar 2021 10:58:54 -0500
Subject: ci: Restore the Module.ExternalData test on Windows

In commit a58e3c7e8b (ci: Skip the Module.ExternalData test on Windows,
2020-10-01, v3.19.0-rc1~59^2) the test was dropped pending further
investigation.  We've now (hopefully) resolved the underlying problem,
so we can restore the test.
---
 .gitlab/ci/ctest_exclusions.cmake | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake
index a68a693..b885a6a 100644
--- a/.gitlab/ci/ctest_exclusions.cmake
+++ b/.gitlab/ci/ctest_exclusions.cmake
@@ -13,15 +13,6 @@ if (CTEST_CMAKE_GENERATOR MATCHES "Visual Studio")
     "^ExternalProjectUpdateSetup$")
 endif ()
 
-if (CMAKE_HOST_WIN32)
-  list(APPEND test_exclusions
-    # This test often fails with an undiagnosed subtle race due to the test
-    # re-using the same objects for many files.  Some copy operations fail
-    # to open their input with ERROR_SHARING_VIOLATION.
-    "^Module.ExternalData$"
-    )
-endif()
-
 string(REPLACE ";" "|" test_exclusions "${test_exclusions}")
 if (test_exclusions)
   set(test_exclusions "(${test_exclusions})")
-- 
cgit v0.12