Skip to content

Commit 313e27e

Browse files
authored
Simplify locking additions (#133)
* Fix update_status race by snapshotting under the lock instead. * Drop unnecessary Lockable wrapping of `tests` It's read-only anyway.
1 parent 3d77459 commit 313e27e

1 file changed

Lines changed: 26 additions & 21 deletions

File tree

src/ParallelTestRunner.jl

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -932,15 +932,15 @@ function runtests(mod::Module, args::ParsedArgs;
932932
filter_tests!(testsuite, args)
933933

934934
# determine test order
935-
tests = Lockable(collect(keys(testsuite)))
936-
@lock tests Random.shuffle!(tests[])
935+
tests = collect(keys(testsuite))
936+
Random.shuffle!(tests)
937937
historical_durations = load_test_history(mod)
938-
@lock tests sort!(tests[], by = x -> -get(historical_durations, x, Inf))
938+
sort!(tests, by = x -> -get(historical_durations, x, Inf))
939939

940940
# determine parallelism
941941
jobs = something(args.jobs, default_njobs())
942-
jobs = @lock tests clamp(jobs, 1, length(tests[]))
943-
@lock tests println(stdout, "Running $(length(tests[])) tests using $jobs parallel jobs. If this is too many concurrent jobs, specify the `--jobs=N` argument to the tests, or set the `JULIA_CPU_THREADS` environment variable.")
942+
jobs = clamp(jobs, 1, length(tests))
943+
println(stdout, "Running $(length(tests)) tests using $jobs parallel jobs. If this is too many concurrent jobs, specify the `--jobs=N` argument to the tests, or set the `JULIA_CPU_THREADS` environment variable.")
944944
!isnothing(args.verbose) && println(stdout, "Available memory: $(Base.format_bytes(available_memory()))")
945945
sem = Base.Semaphore(max(1, jobs))
946946
worker_pool = Channel{Union{Nothing, PTRWorker}}(jobs)
@@ -974,10 +974,10 @@ function runtests(mod::Module, args::ParsedArgs;
974974
# pretty print information about gc and mem usage
975975
testgroupheader = "Test"
976976
workerheader = "(Worker)"
977-
name_align = @lock tests maximum(
977+
name_align = maximum(
978978
[
979979
textwidth(testgroupheader) + textwidth(" ") + textwidth(workerheader);
980-
map(x -> textwidth(x) + 5, tests[])
980+
map(x -> textwidth(x) + 5, tests)
981981
]
982982
)
983983

@@ -1003,16 +1003,19 @@ function runtests(mod::Module, args::ParsedArgs;
10031003
end
10041004

10051005
function update_status()
1006-
# only draw if we have something to show
1007-
@lock(running_tests, isempty(running_tests[])) && return
1008-
completed = @lock results length(results[])
1009-
total = @lock tests length(tests[])
1006+
# take consistent snapshots once, so the rest of this function operates on
1007+
# frozen data rather than racing with workers that mutate these collections
1008+
running_snapshot = @lock running_tests copy(running_tests[])
1009+
isempty(running_snapshot) && return
1010+
results_snapshot = @lock results copy(results[])
1011+
completed = length(results_snapshot)
1012+
total = length(tests)
10101013

10111014
# line 1: empty line
10121015
line1 = ""
10131016

10141017
# line 2: running tests
1015-
test_list = @lock running_tests sort(collect(keys(running_tests[])), by = x -> running_tests[][x])
1018+
test_list = sort(collect(keys(running_snapshot)), by = x -> running_snapshot[x])
10161019
status_parts = map(test_list) do test
10171020
"$test"
10181021
end
@@ -1027,23 +1030,23 @@ function runtests(mod::Module, args::ParsedArgs;
10271030
line3 = "Progress: $completed/$total tests completed"
10281031
if completed > 0
10291032
# estimate per-test time (slightly pessimistic)
1030-
durations_done = @lock results [end_time - start_time for (_, _,_, start_time, end_time) in results[]]
1033+
durations_done = [end_time - start_time for (_, _,_, start_time, end_time) in results_snapshot]
10311034
μ = mean(durations_done)
10321035
σ = length(durations_done) > 1 ? std(durations_done) : 0.0
10331036
est_per_test = μ + 0.5σ
10341037

10351038
est_remaining = 0.0
10361039
## currently-running
1037-
for (test, start_time) in @lock(running_tests, running_tests[])
1040+
for (test, start_time) in running_snapshot
10381041
elapsed = time() - start_time
10391042
duration = get(historical_durations, test, est_per_test)
10401043
est_remaining += max(0.0, duration - elapsed)
10411044
end
10421045
## yet-to-run
1043-
for test in @lock(tests, tests[])
1044-
@lock(running_tests, haskey(running_tests[], test)) && continue
1046+
for test in tests
1047+
haskey(running_snapshot, test) && continue
10451048
# Test is in any completed test
1046-
@lock(results, any(r -> test == r.test, results[])) && continue
1049+
any(r -> test == r.test, results_snapshot) && continue
10471050
est_remaining += get(historical_durations, test, est_per_test)
10481051
end
10491052

@@ -1126,7 +1129,9 @@ function runtests(mod::Module, args::ParsedArgs;
11261129
end
11271130
isa(ex, InterruptException) || rethrow()
11281131
finally
1129-
if @lock running_tests @lock results @lock tests isempty(running_tests[]) && length(results[]) >= length(tests[])
1132+
n_running = @lock running_tests length(running_tests[])
1133+
n_results = @lock results length(results[])
1134+
if n_running == 0 && n_results >= length(tests)
11301135
# XXX: only erase the status if we completed successfully.
11311136
# in other cases we'll have printed "caught interrupt"
11321137
clear_status()
@@ -1138,9 +1143,9 @@ function runtests(mod::Module, args::ParsedArgs;
11381143
# execution
11391144
#
11401145

1141-
tests_to_start = @lock tests Threads.Atomic{Int}(length(tests[]))
1146+
tests_to_start = Threads.Atomic{Int}(length(tests))
11421147
try
1143-
@sync for test in @lock(tests, tests[])
1148+
@sync for test in tests
11441149
push!(worker_tasks, Threads.@spawn begin
11451150
local p = nothing
11461151
acquired = false
@@ -1360,7 +1365,7 @@ function runtests(mod::Module, args::ParsedArgs;
13601365
end
13611366

13621367
# mark remaining or running tests as interrupted
1363-
for test in @lock(tests, tests[])
1368+
for test in tests
13641369
(test in completed_tests) && continue
13651370
testset = create_testset(test)
13661371
Test.record(testset, Test.Error(:test_interrupted, test, nothing, Base.ExceptionStack(NamedTuple[(;exception = "skipped", backtrace = [])]), LineNumberNode(1)))

0 commit comments

Comments
 (0)