|
28 | 28 | # (expected test duration is about 18-180 seconds)
|
29 | 29 | Timer(t -> killjob("KILLING BY THREAD TEST WATCHDOG\n"), 1200)
|
30 | 30 |
|
| 31 | +module ConcurrencyUtilities |
| 32 | + function new_task_nonsticky(f) |
| 33 | + t = Task(f) |
| 34 | + t.sticky = false |
| 35 | + t |
| 36 | + end |
| 37 | + |
| 38 | + """ |
| 39 | + run_concurrently(worker, n)::Nothing |
| 40 | +
|
| 41 | + Run `n` tasks of `worker` concurrently. Return when all workers are done. |
| 42 | + """ |
| 43 | + function run_concurrently(worker, n) |
| 44 | + tasks = map(new_task_nonsticky ∘ Returns(worker), Base.OneTo(n)) |
| 45 | + foreach(schedule, tasks) |
| 46 | + foreach(fetch, tasks) |
| 47 | + end |
| 48 | + |
| 49 | + """ |
| 50 | + run_concurrently_in_new_task(worker, n)::Task |
| 51 | +
|
| 52 | + Return a task that: |
| 53 | + * is not started yet |
| 54 | + * when started, runs `n` tasks of `worker` concurrently |
| 55 | + * returns when all workers are done |
| 56 | + """ |
| 57 | + function run_concurrently_in_new_task(worker, n) |
| 58 | + function f(t) |
| 59 | + run_concurrently(t...) |
| 60 | + end |
| 61 | + new_task_nonsticky(f ∘ Returns((worker, n))) |
| 62 | + end |
| 63 | +end |
| 64 | + |
| 65 | +module AbstractIrrationalExamples |
| 66 | + for n ∈ 0:9 |
| 67 | + name_aa = Symbol(:aa, n) |
| 68 | + name_ab = Symbol(:ab, n) |
| 69 | + name_ba = Symbol(:ba, n) |
| 70 | + name_bb = Symbol(:bb, n) |
| 71 | + @eval begin |
| 72 | + Base.@irrational $name_aa exp(BigFloat(2)^$n) |
| 73 | + Base.@irrational $name_ab exp(BigFloat(2)^-$n) |
| 74 | + Base.@irrational $name_ba exp(-(BigFloat(2)^$n)) |
| 75 | + Base.@irrational $name_bb exp(-(BigFloat(2)^-$n)) |
| 76 | + end |
| 77 | + end |
| 78 | + const examples = ( |
| 79 | + aa0, aa1, aa2, aa3, aa4, aa5, aa6, aa7, aa8, aa9, |
| 80 | + ab0, ab1, ab2, ab3, ab4, ab5, ab6, ab7, ab8, ab9, |
| 81 | + ba0, ba1, ba2, ba3, ba4, ba5, ba6, ba7, ba8, ba9, |
| 82 | + bb0, bb1, bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb9, |
| 83 | + ) |
| 84 | +end |
| 85 | + |
31 | 86 | @testset """threads_exec.jl with JULIA_NUM_THREADS == $(ENV["JULIA_NUM_THREADS"])""" begin
|
32 | 87 |
|
33 | 88 | @test Threads.threadid() == 1
|
@@ -1347,6 +1402,43 @@ end
|
1347 | 1402 | end
|
1348 | 1403 | end
|
1349 | 1404 |
|
| 1405 | +@testset "race on `BigFloat` precision when constructing `Rational` from `AbstractIrrational`" begin |
| 1406 | + function test_racy_rational_from_irrational(::Type{Rational{I}}, c::AbstractIrrational) where {I} |
| 1407 | + function construct() |
| 1408 | + Rational{I}(c) |
| 1409 | + end |
| 1410 | + function is_racy_rational_from_irrational() |
| 1411 | + worker_count = 10 * Threads.nthreads() |
| 1412 | + task = ConcurrencyUtilities.run_concurrently_in_new_task(construct, worker_count) |
| 1413 | + schedule(task) |
| 1414 | + ok = true |
| 1415 | + while !istaskdone(task) |
| 1416 | + for _ ∈ 1:1000000 |
| 1417 | + ok &= precision(BigFloat) === prec |
| 1418 | + end |
| 1419 | + GC.safepoint() |
| 1420 | + yield() |
| 1421 | + end |
| 1422 | + fetch(task) |
| 1423 | + ok |
| 1424 | + end |
| 1425 | + prec = precision(BigFloat) |
| 1426 | + task = ConcurrencyUtilities.new_task_nonsticky(is_racy_rational_from_irrational) |
| 1427 | + schedule(task) |
| 1428 | + ok = fetch(task)::Bool |
| 1429 | + setprecision(BigFloat, prec) |
| 1430 | + ok |
| 1431 | + end |
| 1432 | + @testset "c: $c" for c ∈ AbstractIrrationalExamples.examples |
| 1433 | + Q = Rational{Int128} |
| 1434 | + # metatest: `test_racy_rational_from_irrational` needs the constructor |
| 1435 | + # to not be constant folded away, otherwise it's not testing anything. |
| 1436 | + @test !Core.Compiler.is_foldable(Base.infer_effects(Q, Tuple{typeof(c)})) |
| 1437 | + # test for race |
| 1438 | + @test test_racy_rational_from_irrational(Q, c) |
| 1439 | + end |
| 1440 | +end |
| 1441 | + |
1350 | 1442 | @testset "task time counters" begin
|
1351 | 1443 | @testset "enabled" begin
|
1352 | 1444 | try
|
|
0 commit comments