authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-25 18:38:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-25 18:38:49-07:00
logc7834f274d9c48e1ef155a367cbb598c239b8c7a
tree2bfe99d11f4ea0ced72a6a52c710cc6bc7554fa0
parent1634d45f1d53c8d7bfefa56ab4d2fa4cc8218b6d

stage2: Cache: add debug deadlock detection code


1 files changed, 37 insertions(+), 1 deletions(-)

src/Cache.zig+37-1
...@@ -12,6 +12,14 @@ const mem = std.mem;...@@ -12,6 +12,14 @@ const mem = std.mem;
12const fmt = std.fmt;12const fmt = std.fmt;
13const Allocator = std.mem.Allocator;13const Allocator = std.mem.Allocator;
1414
15/// Process-scoped map keeping track of all locked Cache hashes, to detect deadlocks.
16/// The plan is to enable this for debug builds only, but for now we enable
17/// it always to catch a deadlock.
18var all_cache_digest_set: std.AutoHashMapUnmanaged(BinDigest, void) = .{};
19var all_cache_digest_lock: std.Mutex = .{};
20const want_debug_deadlock = true; // TODO change this for release builds
21const DebugBinDigest = if (want_debug_deadlock) BinDigest else void;
22
15/// Be sure to call `Manifest.deinit` after successful initialization.23/// Be sure to call `Manifest.deinit` after successful initialization.
16pub fn obtain(cache: *const Cache) Manifest {24pub fn obtain(cache: *const Cache) Manifest {
17 return Manifest{25 return Manifest{
...@@ -160,8 +168,15 @@ pub const HashHelper = struct {...@@ -160,8 +168,15 @@ pub const HashHelper = struct {
160168
161pub const Lock = struct {169pub const Lock = struct {
162 manifest_file: fs.File,170 manifest_file: fs.File,
171 debug_bin_digest: DebugBinDigest,
163172
164 pub fn release(lock: *Lock) void {173 pub fn release(lock: *Lock) void {
174 if (want_debug_deadlock) {
175 const held = all_cache_digest_lock.acquire();
176 defer held.release();
177
178 all_cache_digest_set.removeAssertDiscard(lock.debug_bin_digest);
179 }
165 lock.manifest_file.close();180 lock.manifest_file.close();
166 lock.* = undefined;181 lock.* = undefined;
167 }182 }
...@@ -178,6 +193,7 @@ pub const Manifest = struct {...@@ -178,6 +193,7 @@ pub const Manifest = struct {
178 manifest_dirty: bool,193 manifest_dirty: bool,
179 files: std.ArrayListUnmanaged(File) = .{},194 files: std.ArrayListUnmanaged(File) = .{},
180 hex_digest: [hex_digest_len]u8,195 hex_digest: [hex_digest_len]u8,
196 bin_digest: DebugBinDigest = undefined,
181197
182 /// Add a file as a dependency of process being cached. When `hit` is198 /// Add a file as a dependency of process being cached. When `hit` is
183 /// called, the file's contents will be checked to ensure that it matches199 /// called, the file's contents will be checked to ensure that it matches
...@@ -245,6 +261,23 @@ pub const Manifest = struct {...@@ -245,6 +261,23 @@ pub const Manifest = struct {
245 var bin_digest: BinDigest = undefined;261 var bin_digest: BinDigest = undefined;
246 self.hash.hasher.final(&bin_digest);262 self.hash.hasher.final(&bin_digest);
247263
264 if (want_debug_deadlock) {
265 self.bin_digest = bin_digest;
266
267 const held = all_cache_digest_lock.acquire();
268 defer held.release();
269
270 const gop = try all_cache_digest_set.getOrPut(self.cache.gpa, bin_digest);
271 if (gop.found_existing) {
272 std.debug.print("Cache deadlock detected in Cache.hit. Manifest has {d} files:\n", .{self.files.items.len});
273 for (self.files.items) |file| {
274 const p: []const u8 = file.path orelse "(null)";
275 std.debug.print(" file: {s}\n", .{p});
276 }
277 @panic("Cache deadlock detected");
278 }
279 }
280
248 _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable;281 _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable;
249282
250 self.hash.hasher = hasher_init;283 self.hash.hasher = hasher_init;
...@@ -572,7 +605,10 @@ pub const Manifest = struct {...@@ -572,7 +605,10 @@ pub const Manifest = struct {
572 pub fn toOwnedLock(self: *Manifest) Lock {605 pub fn toOwnedLock(self: *Manifest) Lock {
573 const manifest_file = self.manifest_file.?;606 const manifest_file = self.manifest_file.?;
574 self.manifest_file = null;607 self.manifest_file = null;
575 return Lock{ .manifest_file = manifest_file };608 return Lock{
609 .manifest_file = manifest_file,
610 .debug_bin_digest = self.bin_digest,
611 };
576 }612 }
577613
578 /// Releases the manifest file and frees any memory the Manifest was using.614 /// Releases the manifest file and frees any memory the Manifest was using.