Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit fe3499f

Browse files
committed
Lock example_status_persistence_file to prevent race conditions
This introduces file locking to ExampleStatusPersister#persist to avoid a race condition where parallel or unrelated spec runs race to update the same file: - read 1 - read 2 - write 1 - write 2 - write 1 is lost Because we need to read and write the file under the same lock, I inline #write into #persist so that dumped_statuses is called after the file is opened/locked.
1 parent 32690ab commit fe3499f

File tree

1 file changed

+10
-7
lines changed

1 file changed

+10
-7
lines changed

lib/rspec/core/example_status_persister.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@ def initialize(examples, file_name)
2121
end
2222

2323
def persist
24-
write dumped_statuses
24+
RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(@file_name))
25+
# lock the file while reading / persisting to avoid a race
26+
# condition where parallel or unrelated spec runs race to
27+
# update the same file
28+
File.open(@file_name, File::RDWR | File::CREAT | File::LOCK_EX) { |f|
29+
@previous_runs = File.read(f)
30+
f.truncate(0)
31+
f.write(dumped_statuses)
32+
}
2533
end
2634

2735
private
2836

29-
def write(statuses)
30-
RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(@file_name))
31-
File.open(@file_name, "w") { |f| f.write(statuses) }
32-
end
33-
3437
def dumped_statuses
3538
ExampleStatusDumper.dump(merged_statuses)
3639
end
@@ -52,7 +55,7 @@ def statuses_from_this_run
5255
end
5356

5457
def statuses_from_previous_runs
55-
self.class.load_from(@file_name)
58+
ExampleStatusParser.parse(@previous_runs)
5659
end
5760
end
5861

0 commit comments

Comments
 (0)