diff --git a/llvm/include/llvm/Support/LockFileManager.h b/llvm/include/llvm/Support/LockFileManager.h --- a/llvm/include/llvm/Support/LockFileManager.h +++ b/llvm/include/llvm/Support/LockFileManager.h @@ -78,8 +78,8 @@ /// For a shared lock, wait until the owner releases the lock. /// Total timeout for the file to appear is ~1.5 minutes. - /// \param MaxSeconds the maximum wait time per iteration in seconds. - WaitForUnlockResult waitForUnlock(const unsigned MaxSeconds = 40); + /// \param MaxSeconds the maximum total wait time in seconds. + WaitForUnlockResult waitForUnlock(const unsigned MaxSeconds = 90); /// Remove the lock file. This may delete a different lock file than /// the one previously read if there is a race. diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp --- a/llvm/lib/Support/LockFileManager.cpp +++ b/llvm/lib/Support/LockFileManager.cpp @@ -17,12 +17,16 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include +#include #include #include +#include #include #include #include +#include #include + #ifdef _WIN32 #include #endif @@ -295,23 +299,29 @@ if (getState() != LFS_Shared) return Res_Success; -#ifdef _WIN32 - unsigned long Interval = 1; -#else - struct timespec Interval; - Interval.tv_sec = 0; - Interval.tv_nsec = 1000000; -#endif + // Since we don't yet have an event-based method to wait for the lock file, + // implement randomized exponential backoff, similar to Ethernet collision + // algorithm. This improves performance on machines with high core counts + // when the file lock is heavily contended by multiple clang processes + const unsigned long MinWaitDurationMS = 10; + const unsigned long MaxWaitMultiplier = 50; // 500ms max wait + unsigned long WaitMultiplier = 1; + unsigned long ElapsedTimeSeconds = 0; + + std::random_device Device; + std::default_random_engine Engine(Device()); + + auto StartTime = std::chrono::steady_clock::now(); + do { + // FIXME: implement event-based waiting + // Sleep for the designated interval, to allow the owning process time to // finish up and remove the lock file. - // FIXME: Should we hook in to system APIs to get a notification when the - // lock file is deleted? -#ifdef _WIN32 - Sleep(Interval); -#else - nanosleep(&Interval, nullptr); -#endif + std::uniform_int_distribution Distribution(1, + WaitMultiplier); + unsigned long WaitDurationMS = MinWaitDurationMS * Distribution(Engine); + std::this_thread::sleep_for(std::chrono::milliseconds(WaitDurationMS)); if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) == errc::no_such_file_or_directory) { @@ -325,24 +335,16 @@ if (!processStillExecuting((*Owner).first, (*Owner).second)) return Res_OwnerDied; - // Exponentially increase the time we wait for the lock to be removed. -#ifdef _WIN32 - Interval *= 2; -#else - Interval.tv_sec *= 2; - Interval.tv_nsec *= 2; - if (Interval.tv_nsec >= 1000000000) { - ++Interval.tv_sec; - Interval.tv_nsec -= 1000000000; + WaitMultiplier *= 2; + if (WaitMultiplier > MaxWaitMultiplier) { + WaitMultiplier = MaxWaitMultiplier; } -#endif - } while ( -#ifdef _WIN32 - Interval < MaxSeconds * 1000 -#else - Interval.tv_sec < (time_t)MaxSeconds -#endif - ); + + ElapsedTimeSeconds = std::chrono::duration_cast( + std::chrono::steady_clock::now() - StartTime) + .count(); + + } while (ElapsedTimeSeconds < MaxSeconds); // Give up. return Res_Timeout;