| ... | @@ -8,6 +8,7 @@ const mem = @import("mem.zig"); | ... | @@ -8,6 +8,7 @@ const mem = @import("mem.zig"); |
| 8 | const fmt = @import("fmt.zig"); | 8 | const fmt = @import("fmt.zig"); |
| 9 | const Allocator = mem.Allocator; | 9 | const Allocator = mem.Allocator; |
| 10 | const os = @import("os.zig"); | 10 | const os = @import("os.zig"); |
| | 11 | const time = @import("time.zig"); |
| 11 | | 12 | |
| 12 | const base64_encoder = fs.base64_encoder; | 13 | const base64_encoder = fs.base64_encoder; |
| 13 | const base64_decoder = fs.base64_decoder; | 14 | const base64_decoder = fs.base64_decoder; |
| ... | @@ -198,7 +199,10 @@ pub const CacheHash = struct { | ... | @@ -198,7 +199,10 @@ pub const CacheHash = struct { |
| 198 | | 199 | |
| 199 | cache_hash_file.stat = actual_stat; | 200 | cache_hash_file.stat = actual_stat; |
| 200 | | 201 | |
| 201 | // TODO: check for problematic timestamp | 202 | if (is_problematic_timestamp(cache_hash_file.stat.mtime)) { |
| | 203 | cache_hash_file.stat.mtime = 0; |
| | 204 | cache_hash_file.stat.inode = 0; |
| | 205 | } |
| 202 | | 206 | |
| 203 | var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; | 207 | var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 204 | try hash_file(self.alloc, &actual_digest, &this_file); | 208 | try hash_file(self.alloc, &actual_digest, &this_file); |
| ... | @@ -252,7 +256,10 @@ pub const CacheHash = struct { | ... | @@ -252,7 +256,10 @@ pub const CacheHash = struct { |
| 252 | | 256 | |
| 253 | cache_hash_file.stat = try this_file.stat(); | 257 | cache_hash_file.stat = try this_file.stat(); |
| 254 | | 258 | |
| 255 | // TODO: check for problematic timestamp | 259 | if (is_problematic_timestamp(cache_hash_file.stat.mtime)) { |
| | 260 | cache_hash_file.stat.mtime = 0; |
| | 261 | cache_hash_file.stat.inode = 0; |
| | 262 | } |
| 256 | | 263 | |
| 257 | try hash_file(self.alloc, &cache_hash_file.bin_digest, &this_file); | 264 | try hash_file(self.alloc, &cache_hash_file.bin_digest, &this_file); |
| 258 | self.blake3.update(&cache_hash_file.bin_digest); | 265 | self.blake3.update(&cache_hash_file.bin_digest); |
| ... | @@ -317,6 +324,16 @@ fn hash_file(alloc: *Allocator, bin_digest: []u8, handle: *const fs.File) !void | ... | @@ -317,6 +324,16 @@ fn hash_file(alloc: *Allocator, bin_digest: []u8, handle: *const fs.File) !void |
| 317 | blake3.final(bin_digest); | 324 | blake3.final(bin_digest); |
| 318 | } | 325 | } |
| 319 | | 326 | |
| | 327 | /// If the wall clock time, rounded to the same precision as the |
| | 328 | /// mtime, is equal to the mtime, then we cannot rely on this mtime |
| | 329 | /// yet. We will instead save an mtime value that indicates the hash |
| | 330 | /// must be unconditionally computed. |
| | 331 | fn is_problematic_timestamp(file_mtime_ns: i64) bool { |
| | 332 | const now_ms = time.milliTimestamp(); |
| | 333 | const file_mtime_ms = @divFloor(file_mtime_ns, time.millisecond); |
| | 334 | return now_ms == file_mtime_ms; |
| | 335 | } |
| | 336 | |
| 320 | test "cache file and then recall it" { | 337 | test "cache file and then recall it" { |
| 321 | const cwd = fs.cwd(); | 338 | const cwd = fs.cwd(); |
| 322 | | 339 | |
| ... | @@ -360,3 +377,13 @@ test "cache file and then recall it" { | ... | @@ -360,3 +377,13 @@ test "cache file and then recall it" { |
| 360 | try cwd.deleteTree(temp_manifest_dir); | 377 | try cwd.deleteTree(temp_manifest_dir); |
| 361 | try cwd.deleteFile(temp_file); | 378 | try cwd.deleteFile(temp_file); |
| 362 | } | 379 | } |
| | 380 | |
| | 381 | test "give problematic timestamp" { |
| | 382 | const now_ns = @intCast(i64, time.milliTimestamp() * time.millisecond); |
| | 383 | debug.assert(is_problematic_timestamp(now_ns)); |
| | 384 | } |
| | 385 | |
| | 386 | test "give nonproblematic timestamp" { |
| | 387 | const now_ns = @intCast(i64, time.milliTimestamp() * time.millisecond) - 1000; |
| | 388 | debug.assert(!is_problematic_timestamp(now_ns)); |
| | 389 | } |