From 34286530b7c8beb070368b54abd8ec67a7344119 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Apr 2023 13:08:30 -0700 Subject: [PATCH] Cache: fix multi-process race condition on macOS This fixes `.INVAL => unreachable` being triggered by the cache system on macOS when multiple processes race to create the same compilation. The problem is that when two processes race to create a file, it sometimes returns ENOENT even though that error code is nonsensical for this situation. Commit 2b0929929d67e222ca6a9523a3a594ed456c4a51 purportedly solved this, but it did not open the file with write permissions, leading to the EINVAL panic later on. This commit remedies the situation by introducing a loop and simply retrying when the ENOENT occurs. --- lib/std/Build/Cache.zig | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index f8c83451cb..3b67f4b24c 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -434,24 +434,29 @@ pub const Manifest = struct { } } } else { - if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ - .read = true, - .truncate = false, - .lock = .Exclusive, - .lock_nonblocking = self.want_shared_lock, - })) |manifest_file| { - self.manifest_file = manifest_file; - self.have_exclusive_lock = true; - } else |err| switch (err) { - // There are no dir components, so you would think that this was - // unreachable, however we have observed on macOS two processes racing - // to do openat() with O_CREAT manifest in ENOENT. - error.WouldBlock, error.FileNotFound => { - self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ - .lock = .Shared, - }); - }, - else => |e| return e, + while (true) { + if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ + .read = true, + .truncate = false, + .lock = .Exclusive, + .lock_nonblocking = self.want_shared_lock, + })) |manifest_file| { + self.manifest_file = manifest_file; + self.have_exclusive_lock = true; + break; + } else |err| switch (err) { + error.WouldBlock => { + self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ + .lock = .Shared, + }); + break; + }, + // There are no dir components, so you would think that this was + // unreachable, however we have observed on macOS two processes racing + // to do openat() with O_CREAT manifest in ENOENT. + error.FileNotFound => continue, + else => |e| return e, + } } }