Commit dc18b8174a

Casey Banner <kcbanner@gmail.com>
2023-01-04 20:51:43
Fix another LockViolation case on Windows (#14162)
- Add an assert that an exclusive lock is help to writeManifest - Only call writeManifest in updateCObject if an exclusive lock is held - cache: fixup test to verify hits don't take an exclusive lock, instead of writing the manifest
1 parent 7d538d6
Changed files (2)
src/Cache.zig
@@ -735,6 +735,8 @@ pub const Manifest = struct {
     /// If `want_shared_lock` is true, this function automatically downgrades the
     /// lock from exclusive to shared.
     pub fn writeManifest(self: *Manifest) !void {
+        assert(self.have_exclusive_lock);
+
         const manifest_file = self.manifest_file.?;
         if (self.manifest_dirty) {
             self.manifest_dirty = false;
@@ -936,7 +938,7 @@ test "cache file and then recall it" {
             try testing.expect(try ch.hit());
             digest2 = ch.final();
 
-            try ch.writeManifest();
+            try testing.expectEqual(false, ch.have_exclusive_lock);
         }
 
         try testing.expectEqual(digest1, digest2);
@@ -1062,7 +1064,7 @@ test "no file inputs" {
 
         try testing.expect(try man.hit());
         digest2 = man.final();
-        try man.writeManifest();
+        try testing.expectEqual(false, man.have_exclusive_lock);
     }
 
     try testing.expectEqual(digest1, digest2);
@@ -1124,7 +1126,7 @@ test "Manifest with files added after initial hash work" {
             try testing.expect(try ch.hit());
             digest2 = ch.final();
 
-            try ch.writeManifest();
+            try testing.expectEqual(false, ch.have_exclusive_lock);
         }
         try testing.expect(mem.eql(u8, &digest1, &digest2));
 
src/Compilation.zig
@@ -3693,13 +3693,15 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
         break :digest digest;
     } else man.final();
 
-    // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
-    // 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.writeManifest() catch |err| {
-        log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
-    };
+    if (man.have_exclusive_lock) {
+        // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
+        // 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.writeManifest() catch |err| {
+            log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
+        };
+    }
 
     const out_zig_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
         "o", &digest, cimport_zig_basename,
@@ -4160,13 +4162,15 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
         break :blk digest;
     };
 
-    // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
-    // 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.writeManifest() catch |err| {
-        log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) });
-    };
+    if (man.have_exclusive_lock) {
+        // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
+        // 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.writeManifest() catch |err| {
+            log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) });
+        };
+    }
 
     const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });