| author | |
| committer | |
| log | a4418a8fd6663f532a021c3ad2ae807a6ccd62cc |
| tree | 4215b70f84b4dceb2d78bf2014a6fdd334e7d158 |
| parent | bb957a976d214915c51d72a8112ef905a3777602 |
- C compilation flows didn't hold an exclusive lock on the cache manifest file when writing to it in all cases
- On windows, explicitly unlock the file lock before closing it3 files changed, 23 insertions(+), 1 deletions(-)
src/Cache.zig+18-1| ... | ... | @@ -170,6 +170,12 @@ pub const Lock = struct { |
| 170 | 170 | manifest_file: fs.File, |
| 171 | 171 | |
| 172 | 172 | pub fn release(lock: *Lock) void { |
| 173 | if (builtin.os.tag == .windows) { | |
| 174 | // Windows does not guarantee that locks are immediately unlocked when | |
| 175 | // the file handle is closed. See LockFileEx documentation. | |
| 176 | lock.manifest_file.unlock(); | |
| 177 | } | |
| 178 | ||
| 173 | 179 | lock.manifest_file.close(); |
| 174 | 180 | lock.* = undefined; |
| 175 | 181 | } |
| ... | ... | @@ -480,7 +486,10 @@ pub const Manifest = struct { |
| 480 | 486 | return false; |
| 481 | 487 | } |
| 482 | 488 | |
| 483 | try self.downgradeToSharedLock(); | |
| 489 | if (self.want_shared_lock) { | |
| 490 | try self.downgradeToSharedLock(); | |
| 491 | } | |
| 492 | ||
| 484 | 493 | return true; |
| 485 | 494 | } |
| 486 | 495 | |
| ... | ... | @@ -770,11 +779,13 @@ pub const Manifest = struct { |
| 770 | 779 | const manifest_file = self.manifest_file.?; |
| 771 | 780 | try manifest_file.downgradeLock(); |
| 772 | 781 | } |
| 782 | ||
| 773 | 783 | self.have_exclusive_lock = false; |
| 774 | 784 | } |
| 775 | 785 | |
| 776 | 786 | fn upgradeToExclusiveLock(self: *Manifest) !void { |
| 777 | 787 | if (self.have_exclusive_lock) return; |
| 788 | assert(self.manifest_file != null); | |
| 778 | 789 | |
| 779 | 790 | // WASI does not currently support flock, so we bypass it here. |
| 780 | 791 | // TODO: If/when flock is supported on WASI, this check should be removed. |
| ... | ... | @@ -796,6 +807,7 @@ pub const Manifest = struct { |
| 796 | 807 | const lock: Lock = .{ |
| 797 | 808 | .manifest_file = self.manifest_file.?, |
| 798 | 809 | }; |
| 810 | ||
| 799 | 811 | self.manifest_file = null; |
| 800 | 812 | return lock; |
| 801 | 813 | } |
| ... | ... | @@ -805,6 +817,11 @@ pub const Manifest = struct { |
| 805 | 817 | /// Don't forget to call `writeManifest` before this! |
| 806 | 818 | pub fn deinit(self: *Manifest) void { |
| 807 | 819 | if (self.manifest_file) |file| { |
| 820 | if (builtin.os.tag == .windows) { | |
| 821 | // See Lock.release for why this is required on Windows | |
| 822 | file.unlock(); | |
| 823 | } | |
| 824 | ||
| 808 | 825 | file.close(); |
| 809 | 826 | } |
| 810 | 827 | for (self.files.items) |*file| { |
src/Compilation.zig+4| ... | ... | @@ -3580,6 +3580,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 3580 | 3580 | const cimport_zig_basename = "cimport.zig"; |
| 3581 | 3581 | |
| 3582 | 3582 | var man = comp.obtainCObjectCacheManifest(); |
| 3583 | man.want_shared_lock = false; | |
| 3583 | 3584 | defer man.deinit(); |
| 3584 | 3585 | |
| 3585 | 3586 | 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 { |
| 3697 | 3698 | // possible we had a hit and the manifest is dirty, for example if the file mtime changed but |
| 3698 | 3699 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update |
| 3699 | 3700 | // it to prevent doing a full file content comparison the next time around. |
| 3701 | man.want_shared_lock = true; | |
| 3700 | 3702 | man.writeManifest() catch |err| { |
| 3701 | 3703 | log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); |
| 3702 | 3704 | }; |
| ... | ... | @@ -3871,6 +3873,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3871 | 3873 | } |
| 3872 | 3874 | |
| 3873 | 3875 | var man = comp.obtainCObjectCacheManifest(); |
| 3876 | man.want_shared_lock = false; | |
| 3874 | 3877 | defer man.deinit(); |
| 3875 | 3878 | |
| 3876 | 3879 | man.hash.add(comp.clang_preprocessor_mode); |
| ... | ... | @@ -4164,6 +4167,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4164 | 4167 | // possible we had a hit and the manifest is dirty, for example if the file mtime changed but |
| 4165 | 4168 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update |
| 4166 | 4169 | // it to prevent doing a full file content comparison the next time around. |
| 4170 | man.want_shared_lock = true; | |
| 4167 | 4171 | man.writeManifest() catch |err| { |
| 4168 | 4172 | log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); |
| 4169 | 4173 | }; |
src/main.zig+1| ... | ... | @@ -3429,6 +3429,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool, stage |
| 3429 | 3429 | const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); |
| 3430 | 3430 | |
| 3431 | 3431 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); |
| 3432 | man.want_shared_lock = false; | |
| 3432 | 3433 | defer if (enable_cache) man.deinit(); |
| 3433 | 3434 | |
| 3434 | 3435 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |