diff --git a/src/Cache.zig b/src/Cache.zig index da1e056644..850a28b42e 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -170,6 +170,12 @@ pub const Lock = struct { manifest_file: fs.File, pub fn release(lock: *Lock) void { + if (builtin.os.tag == .windows) { + // Windows does not guarantee that locks are immediately unlocked when + // the file handle is closed. See LockFileEx documentation. + lock.manifest_file.unlock(); + } + lock.manifest_file.close(); lock.* = undefined; } @@ -480,7 +486,10 @@ pub const Manifest = struct { return false; } - try self.downgradeToSharedLock(); + if (self.want_shared_lock) { + try self.downgradeToSharedLock(); + } + return true; } @@ -770,11 +779,13 @@ pub const Manifest = struct { const manifest_file = self.manifest_file.?; try manifest_file.downgradeLock(); } + self.have_exclusive_lock = false; } fn upgradeToExclusiveLock(self: *Manifest) !void { if (self.have_exclusive_lock) return; + assert(self.manifest_file != null); // WASI does not currently support flock, so we bypass it here. // TODO: If/when flock is supported on WASI, this check should be removed. @@ -796,6 +807,7 @@ pub const Manifest = struct { const lock: Lock = .{ .manifest_file = self.manifest_file.?, }; + self.manifest_file = null; return lock; } @@ -805,6 +817,11 @@ pub const Manifest = struct { /// Don't forget to call `writeManifest` before this! pub fn deinit(self: *Manifest) void { if (self.manifest_file) |file| { + if (builtin.os.tag == .windows) { + // See Lock.release for why this is required on Windows + file.unlock(); + } + file.close(); } for (self.files.items) |*file| { diff --git a/src/Compilation.zig b/src/Compilation.zig index 9ba2f77311..9aa4282e79 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3580,6 +3580,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const cimport_zig_basename = "cimport.zig"; var man = comp.obtainCObjectCacheManifest(); + man.want_shared_lock = false; defer man.deinit(); const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1; @@ -3697,6 +3698,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { // possible we had a hit and the manifest is dirty, for example if the file mtime changed but // the contents were the same, we hit the cache but the manifest is dirty and we need to update // it to prevent doing a full file content comparison the next time around. + man.want_shared_lock = true; man.writeManifest() catch |err| { log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); }; @@ -3871,6 +3873,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P } var man = comp.obtainCObjectCacheManifest(); + man.want_shared_lock = false; defer man.deinit(); man.hash.add(comp.clang_preprocessor_mode); @@ -4164,6 +4167,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P // possible we had a hit and the manifest is dirty, for example if the file mtime changed but // the contents were the same, we hit the cache but the manifest is dirty and we need to update // it to prevent doing a full file content comparison the next time around. + man.want_shared_lock = true; man.writeManifest() catch |err| { log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); }; diff --git a/src/main.zig b/src/main.zig index 18852d5b5e..3c39c46a0a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3429,6 +3429,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool, stage const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); + man.want_shared_lock = false; defer if (enable_cache) man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects