| author | |
| committer | |
| log | 8ccb9a6ad327a4d7fbc321b33d4aa66a27a1f5ee |
| tree | 13556a64c4a9af587f12605085b4c880334772e3 |
| parent | 14416b522e697997f21dd634bfdc70fbff59471b |
| signature |
- 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| ... | @@ -232,6 +232,12 @@ pub const Lock = struct { | ... | @@ -232,6 +232,12 @@ pub const Lock = struct { |
| 232 | manifest_file: fs.File, | 232 | manifest_file: fs.File, |
| 233 | 233 | ||
| 234 | pub fn release(lock: *Lock) void { | 234 | pub fn release(lock: *Lock) void { |
| 235 | if (builtin.os.tag == .windows) { | ||
| 236 | // Windows does not guarantee that locks are immediately unlocked when | ||
| 237 | // the file handle is closed. See LockFileEx documentation. | ||
| 238 | lock.manifest_file.unlock(); | ||
| 239 | } | ||
| 240 | |||
| 235 | lock.manifest_file.close(); | 241 | lock.manifest_file.close(); |
| 236 | lock.* = undefined; | 242 | lock.* = undefined; |
| 237 | } | 243 | } |
| ... | @@ -554,7 +560,10 @@ pub const Manifest = struct { | ... | @@ -554,7 +560,10 @@ pub const Manifest = struct { |
| 554 | return false; | 560 | return false; |
| 555 | } | 561 | } |
| 556 | 562 | ||
| 557 | try self.downgradeToSharedLock(); | 563 | if (self.want_shared_lock) { |
| 564 | try self.downgradeToSharedLock(); | ||
| 565 | } | ||
| 566 | |||
| 558 | return true; | 567 | return true; |
| 559 | } | 568 | } |
| 560 | 569 | ||
| ... | @@ -866,11 +875,13 @@ pub const Manifest = struct { | ... | @@ -866,11 +875,13 @@ pub const Manifest = struct { |
| 866 | const manifest_file = self.manifest_file.?; | 875 | const manifest_file = self.manifest_file.?; |
| 867 | try manifest_file.downgradeLock(); | 876 | try manifest_file.downgradeLock(); |
| 868 | } | 877 | } |
| 878 | |||
| 869 | self.have_exclusive_lock = false; | 879 | self.have_exclusive_lock = false; |
| 870 | } | 880 | } |
| 871 | 881 | ||
| 872 | fn upgradeToExclusiveLock(self: *Manifest) !void { | 882 | fn upgradeToExclusiveLock(self: *Manifest) !void { |
| 873 | if (self.have_exclusive_lock) return; | 883 | if (self.have_exclusive_lock) return; |
| 884 | assert(self.manifest_file != null); | ||
| 874 | 885 | ||
| 875 | // WASI does not currently support flock, so we bypass it here. | 886 | // WASI does not currently support flock, so we bypass it here. |
| 876 | // TODO: If/when flock is supported on WASI, this check should be removed. | 887 | // TODO: If/when flock is supported on WASI, this check should be removed. |
| ... | @@ -892,6 +903,7 @@ pub const Manifest = struct { | ... | @@ -892,6 +903,7 @@ pub const Manifest = struct { |
| 892 | const lock: Lock = .{ | 903 | const lock: Lock = .{ |
| 893 | .manifest_file = self.manifest_file.?, | 904 | .manifest_file = self.manifest_file.?, |
| 894 | }; | 905 | }; |
| 906 | |||
| 895 | self.manifest_file = null; | 907 | self.manifest_file = null; |
| 896 | return lock; | 908 | return lock; |
| 897 | } | 909 | } |
| ... | @@ -901,6 +913,11 @@ pub const Manifest = struct { | ... | @@ -901,6 +913,11 @@ pub const Manifest = struct { |
| 901 | /// Don't forget to call `writeManifest` before this! | 913 | /// Don't forget to call `writeManifest` before this! |
| 902 | pub fn deinit(self: *Manifest) void { | 914 | pub fn deinit(self: *Manifest) void { |
| 903 | if (self.manifest_file) |file| { | 915 | if (self.manifest_file) |file| { |
| 916 | if (builtin.os.tag == .windows) { | ||
| 917 | // See Lock.release for why this is required on Windows | ||
| 918 | file.unlock(); | ||
| 919 | } | ||
| 920 | |||
| 904 | file.close(); | 921 | file.close(); |
| 905 | } | 922 | } |
| 906 | for (self.files.items) |*file| { | 923 | for (self.files.items) |*file| { |
src/Compilation.zig+4| ... | @@ -3565,6 +3565,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -3565,6 +3565,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 3565 | const cimport_zig_basename = "cimport.zig"; | 3565 | const cimport_zig_basename = "cimport.zig"; |
| 3566 | 3566 | ||
| 3567 | var man = comp.obtainCObjectCacheManifest(); | 3567 | var man = comp.obtainCObjectCacheManifest(); |
| 3568 | man.want_shared_lock = false; | ||
| 3568 | defer man.deinit(); | 3569 | defer man.deinit(); |
| 3569 | 3570 | ||
| 3570 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects | 3571 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |
| ... | @@ -3678,6 +3679,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -3678,6 +3679,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 3678 | // possible we had a hit and the manifest is dirty, for example if the file mtime changed but | 3679 | // possible we had a hit and the manifest is dirty, for example if the file mtime changed but |
| 3679 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update | 3680 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update |
| 3680 | // it to prevent doing a full file content comparison the next time around. | 3681 | // it to prevent doing a full file content comparison the next time around. |
| 3682 | man.want_shared_lock = true; | ||
| 3681 | man.writeManifest() catch |err| { | 3683 | man.writeManifest() catch |err| { |
| 3682 | log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); | 3684 | log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); |
| 3683 | }; | 3685 | }; |
| ... | @@ -3852,6 +3854,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -3852,6 +3854,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3852 | } | 3854 | } |
| 3853 | 3855 | ||
| 3854 | var man = comp.obtainCObjectCacheManifest(); | 3856 | var man = comp.obtainCObjectCacheManifest(); |
| 3857 | man.want_shared_lock = false; | ||
| 3855 | defer man.deinit(); | 3858 | defer man.deinit(); |
| 3856 | 3859 | ||
| 3857 | man.hash.add(comp.clang_preprocessor_mode); | 3860 | man.hash.add(comp.clang_preprocessor_mode); |
| ... | @@ -4147,6 +4150,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -4147,6 +4150,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4147 | // possible we had a hit and the manifest is dirty, for example if the file mtime changed but | 4150 | // possible we had a hit and the manifest is dirty, for example if the file mtime changed but |
| 4148 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update | 4151 | // the contents were the same, we hit the cache but the manifest is dirty and we need to update |
| 4149 | // it to prevent doing a full file content comparison the next time around. | 4152 | // it to prevent doing a full file content comparison the next time around. |
| 4153 | man.want_shared_lock = true; | ||
| 4150 | man.writeManifest() catch |err| { | 4154 | man.writeManifest() catch |err| { |
| 4151 | log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); | 4155 | log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); |
| 4152 | }; | 4156 | }; |
src/main.zig+1| ... | @@ -3505,6 +3505,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3505,6 +3505,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3505 | const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); | 3505 | const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); |
| 3506 | 3506 | ||
| 3507 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); | 3507 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); |
| 3508 | man.want_shared_lock = false; | ||
| 3508 | defer if (enable_cache) man.deinit(); | 3509 | defer if (enable_cache) man.deinit(); |
| 3509 | 3510 | ||
| 3510 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects | 3511 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |