authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-09 18:55:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-09 22:10:31-07:00
log4da83feccb4b0e59afdcce9796b08cc4fc8346ae
tree789f655f262a31da1495e7ce04735a45eed078ab
parent72ee042ab0c6dc823f839bca6d1511a144f62b49

Cache: improvements to previous commit

* put `recent_problematic_timestamp` onto `Cache` so that it can be shared by multiple Manifest instances. * make `isProblematicTimestamp` return true on any filesystem error. * save 1 syscall by using truncate=true in createFile instead of calling `setEndPos`.

1 files changed, 28 insertions(+), 32 deletions(-)

src/Cache.zig+28-32
......@@ -1,6 +1,7 @@
11gpa: Allocator,
22manifest_dir: fs.Dir,
33hash: HashHelper = .{},
4recent_problematic_timestamp: i128 = 0,
45
56const Cache = @This();
67const std = @import("std");
......@@ -16,7 +17,7 @@ const Compilation = @import("Compilation.zig");
1617const log = std.log.scoped(.cache);
1718
1819/// Be sure to call `Manifest.deinit` after successful initialization.
19pub fn obtain(cache: *const Cache) Manifest {
20pub fn obtain(cache: *Cache) Manifest {
2021 return Manifest{
2122 .cache = cache,
2223 .hash = cache.hash,
......@@ -170,7 +171,7 @@ pub const Lock = struct {
170171/// This is not a general-purpose cache.
171172/// It is designed to be fast and simple, not to withstand attacks using specially-crafted input.
172173pub const Manifest = struct {
173 cache: *const Cache,
174 cache: *Cache,
174175 /// Current state for incremental hashing.
175176 hash: HashHelper,
176177 manifest_file: ?fs.File,
......@@ -187,9 +188,6 @@ pub const Manifest = struct {
187188 /// of the files listed in the manifest.
188189 failed_file_index: ?usize = null,
189190
190 /// most recent problematic timestamp
191 recent_problematic_timestamp: i128 = 0,
192
193191 /// Add a file as a dependency of process being cached. When `hit` is
194192 /// called, the file's contents will be checked to ensure that it matches
195193 /// the contents from previous times.
......@@ -417,7 +415,7 @@ pub const Manifest = struct {
417415
418416 cache_hash_file.stat = actual_stat;
419417
420 if (try self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
418 if (self.cache.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
421419 // The actual file has an unreliable timestamp, force it to be hashed
422420 cache_hash_file.stat.mtime = 0;
423421 cache_hash_file.stat.inode = 0;
......@@ -489,7 +487,7 @@ pub const Manifest = struct {
489487
490488 ch_file.stat = try file.stat();
491489
492 if (try self.isProblematicTimestamp(ch_file.stat.mtime)) {
490 if (self.cache.isProblematicTimestamp(ch_file.stat.mtime)) {
493491 // The actual file has an unreliable timestamp, force it to be hashed
494492 ch_file.stat.mtime = 0;
495493 ch_file.stat.inode = 0;
......@@ -684,26 +682,6 @@ pub const Manifest = struct {
684682 self.have_exclusive_lock = true;
685683 }
686684
687 // Create/Write a file, close it, then grab its stat.mtime timestamp.
688 fn isProblematicTimestamp(self: *Manifest, file_time: i128) !bool {
689
690 // PERF: Check if the file_time is prior to the most recent problematic timestamp
691 // and break out early if so (avoids an I/O to update the recent_problematic_timestamp)
692 if (file_time < self.recent_problematic_timestamp)
693 return false;
694
695 var timestamp_file = try self.cache.manifest_dir.createFile("filetimestamp.tmp", .{
696 .read = true,
697 .truncate = false,
698 });
699 defer timestamp_file.close();
700 try timestamp_file.setEndPos(0);
701
702 self.recent_problematic_timestamp = (try timestamp_file.stat()).mtime;
703
704 return (file_time >= self.recent_problematic_timestamp);
705 }
706
707685 /// Obtain only the data needed to maintain a lock on the manifest file.
708686 /// The `Manifest` remains safe to deinit.
709687 /// Don't forget to call `writeManifest` before this!
......@@ -766,16 +744,34 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
766744 hasher.final(bin_digest);
767745}
768746
747/// Create/Write a file, grab its stat.mtime timestamp, then close it.
748/// If any filesystem errors occur, this function returns `true`.
749fn isProblematicTimestamp(cache: *Cache, file_time: i128) bool {
750 // If the file_time is prior to the most recent problematic timestamp
751 // then we don't need to access the filesystem.
752 if (file_time < cache.recent_problematic_timestamp)
753 return false;
754
755 var file = cache.manifest_dir.createFile("timestamp", .{
756 .read = true,
757 .truncate = true,
758 }) catch return true;
759 defer file.close();
760
761 cache.recent_problematic_timestamp = (file.stat() catch return true).mtime;
762
763 return file_time >= cache.recent_problematic_timestamp;
764}
765
769766// Create/Write a file, close it, then grab its stat.mtime timestamp.
770767fn testGetCurrentFileTimestamp() !i128 {
771 var timestamp_file = try fs.cwd().createFile("zig-cache/filetimestamp.tmp", .{
768 var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{
772769 .read = true,
773 .truncate = false,
770 .truncate = true,
774771 });
775 defer timestamp_file.close();
776 try timestamp_file.setEndPos(0);
772 defer file.close();
777773
778 return (try timestamp_file.stat()).mtime;
774 return (try file.stat()).mtime;
779775}
780776
781777test "cache file and then recall it" {