@@ -3538,44 +3538,61 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3538
3538
const cache_directory = if (want_local_cache ) mod .local_zir_cache else mod .global_zir_cache ;
3539
3539
const zir_dir = cache_directory .handle ;
3540
3540
3541
- var cache_file : ? std.fs.File = null ;
3542
- defer if (cache_file ) | f | f .close ();
3543
-
3544
3541
// Determine whether we need to reload the file from disk and redo parsing and AstGen.
3545
- switch (file .status ) {
3546
- .never_loaded , .retryable_failure = > cached : {
3542
+ var lock : std.fs.File.Lock = switch (file .status ) {
3543
+ .never_loaded , .retryable_failure = > lock : {
3547
3544
// First, load the cached ZIR code, if any.
3548
3545
log .debug ("AstGen checking cache: {s} (local={}, digest={s})" , .{
3549
3546
file .sub_file_path , want_local_cache , & digest ,
3550
3547
});
3551
3548
3552
- // We ask for a lock in order to coordinate with other zig processes.
3553
- // If another process is already working on this file, we will get the cached
3554
- // version. Likewise if we're working on AstGen and another process asks for
3555
- // the cached file, they'll get it.
3556
- cache_file = zir_dir .openFile (& digest , .{ .lock = .Shared }) catch | err | switch (err ) {
3557
- error .PathAlreadyExists = > unreachable , // opening for reading
3558
- error .NoSpaceLeft = > unreachable , // opening for reading
3559
- error .NotDir = > unreachable , // no dir components
3560
- error .InvalidUtf8 = > unreachable , // it's a hex encoded name
3561
- error .BadPathName = > unreachable , // it's a hex encoded name
3562
- error .NameTooLong = > unreachable , // it's a fixed size name
3563
- error .PipeBusy = > unreachable , // it's not a pipe
3564
- error .WouldBlock = > unreachable , // not asking for non-blocking I/O
3565
-
3566
- error .SymLinkLoop ,
3567
- error .FileNotFound ,
3568
- error .Unexpected ,
3569
- = > break :cached ,
3570
-
3571
- else = > | e | return e , // Retryable errors are handled at callsite.
3572
- };
3549
+ break :lock .Shared ;
3550
+ },
3551
+ .parse_failure , .astgen_failure , .success_zir = > lock : {
3552
+ const unchanged_metadata =
3553
+ stat .size == file .stat .size and
3554
+ stat .mtime == file .stat .mtime and
3555
+ stat .inode == file .stat .inode ;
3556
+
3557
+ if (unchanged_metadata ) {
3558
+ log .debug ("unmodified metadata of file: {s}" , .{file .sub_file_path });
3559
+ return ;
3560
+ }
3561
+
3562
+ log .debug ("metadata changed: {s}" , .{file .sub_file_path });
3563
+
3564
+ break :lock .Exclusive ;
3565
+ },
3566
+ };
3567
+
3568
+ // We ask for a lock in order to coordinate with other zig processes.
3569
+ // If another process is already working on this file, we will get the cached
3570
+ // version. Likewise if we're working on AstGen and another process asks for
3571
+ // the cached file, they'll get it.
3572
+ const cache_file = zir_dir .createFile (& digest , .{
3573
+ .read = true ,
3574
+ .truncate = false ,
3575
+ .lock = lock ,
3576
+ }) catch | err | switch (err ) {
3577
+ error .NotDir = > unreachable , // no dir components
3578
+ error .InvalidUtf8 = > unreachable , // it's a hex encoded name
3579
+ error .BadPathName = > unreachable , // it's a hex encoded name
3580
+ error .NameTooLong = > unreachable , // it's a fixed size name
3581
+ error .PipeBusy = > unreachable , // it's not a pipe
3582
+ error .WouldBlock = > unreachable , // not asking for non-blocking I/O
3583
+ error .FileNotFound = > unreachable , // no dir components
3584
+
3585
+ else = > | e | return e , // Retryable errors are handled at callsite.
3586
+ };
3587
+ defer cache_file .close ();
3573
3588
3589
+ while (true ) {
3590
+ update : {
3574
3591
// First we read the header to determine the lengths of arrays.
3575
- const header = cache_file .? . reader ().readStruct (Zir .Header ) catch | err | switch (err ) {
3592
+ const header = cache_file .reader ().readStruct (Zir .Header ) catch | err | switch (err ) {
3576
3593
// This can happen if Zig bails out of this function between creating
3577
3594
// the cached file and writing it.
3578
- error .EndOfStream = > break :cached ,
3595
+ error .EndOfStream = > break :update ,
3579
3596
else = > | e | return e ,
3580
3597
};
3581
3598
const unchanged_metadata =
@@ -3585,7 +3602,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3585
3602
3586
3603
if (! unchanged_metadata ) {
3587
3604
log .debug ("AstGen cache stale: {s}" , .{file .sub_file_path });
3588
- break :cached ;
3605
+ break :update ;
3589
3606
}
3590
3607
log .debug ("AstGen cache hit: {s} instructions_len={d}" , .{
3591
3608
file .sub_file_path , header .instructions_len ,
@@ -3637,13 +3654,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3637
3654
.iov_len = header .extra_len * 4 ,
3638
3655
},
3639
3656
};
3640
- const amt_read = try cache_file .? . readvAll (& iovecs );
3657
+ const amt_read = try cache_file .readvAll (& iovecs );
3641
3658
const amt_expected = zir .instructions .len * 9 +
3642
3659
zir .string_bytes .len +
3643
3660
zir .extra .len * 4 ;
3644
3661
if (amt_read != amt_expected ) {
3645
3662
log .warn ("unexpected EOF reading cached ZIR for {s}" , .{file .sub_file_path });
3646
- break :cached ;
3663
+ break :update ;
3647
3664
}
3648
3665
if (data_has_safety_tag ) {
3649
3666
const tags = zir .instructions .items (.tag );
@@ -3679,42 +3696,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3679
3696
return error .AnalysisFail ;
3680
3697
}
3681
3698
return ;
3682
- },
3683
- .parse_failure , .astgen_failure , .success_zir = > {
3684
- const unchanged_metadata =
3685
- stat .size == file .stat .size and
3686
- stat .mtime == file .stat .mtime and
3687
- stat .inode == file .stat .inode ;
3688
-
3689
- if (unchanged_metadata ) {
3690
- log .debug ("unmodified metadata of file: {s}" , .{file .sub_file_path });
3691
- return ;
3692
- }
3699
+ }
3693
3700
3694
- log .debug ("metadata changed: {s}" , .{file .sub_file_path });
3695
- },
3696
- }
3697
- if (cache_file ) | f | {
3698
- f .close ();
3699
- cache_file = null ;
3701
+ // If we already have the exclusive lock then it is our job to update.
3702
+ if (builtin .os .tag == .wasi or lock == .Exclusive ) break ;
3703
+ // Otherwise, unlock to give someone a chance to get the exclusive lock
3704
+ // and then upgrade to an exclusive lock.
3705
+ cache_file .unlock ();
3706
+ lock = .Exclusive ;
3707
+ try cache_file .lock (lock );
3700
3708
}
3701
- cache_file = zir_dir .createFile (& digest , .{ .lock = .Exclusive }) catch | err | switch (err ) {
3702
- error .NotDir = > unreachable , // no dir components
3703
- error .InvalidUtf8 = > unreachable , // it's a hex encoded name
3704
- error .BadPathName = > unreachable , // it's a hex encoded name
3705
- error .NameTooLong = > unreachable , // it's a fixed size name
3706
- error .PipeBusy = > unreachable , // it's not a pipe
3707
- error .WouldBlock = > unreachable , // not asking for non-blocking I/O
3708
- error .FileNotFound = > unreachable , // no dir components
3709
3709
3710
- else = > | e | {
3711
- const pkg_path = file .pkg .root_src_directory .path orelse "." ;
3712
- const cache_path = cache_directory .path orelse "." ;
3713
- log .warn ("unable to save cached ZIR code for {s}/{s} to {s}/{s}: {s}" , .{
3714
- pkg_path , file .sub_file_path , cache_path , & digest , @errorName (e ),
3715
- });
3716
- return ;
3717
- },
3710
+ // The cache is definitely stale so delete the contents to avoid an underwrite later.
3711
+ cache_file .setEndPos (0 ) catch | err | switch (err ) {
3712
+ error .FileTooBig = > unreachable , // 0 is not too big
3713
+
3714
+ else = > | e | return e ,
3718
3715
};
3719
3716
3720
3717
mod .lockAndClearFileCompileError (file );
@@ -3871,7 +3868,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3871
3868
.iov_len = file .zir .extra .len * 4 ,
3872
3869
},
3873
3870
};
3874
- cache_file .? . writevAll (& iovecs ) catch | err | {
3871
+ cache_file .writevAll (& iovecs ) catch | err | {
3875
3872
const pkg_path = file .pkg .root_src_directory .path orelse "." ;
3876
3873
const cache_path = cache_directory .path orelse "." ;
3877
3874
log .warn ("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}" , .{
0 commit comments