Skip to content

Commit 53ca0bf

Browse files
expand threads_exec tests to cover interactive threads
fix test thread indexing make threads_exec work being called from :interactive and :default pools add tests for threaded construct default pools etc.
1 parent d91238a commit 53ca0bf

File tree

2 files changed

+77
-52
lines changed

2 files changed

+77
-52
lines changed

test/threads.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,24 @@ let e = Event(true), started1 = Event(true), started2 = Event(true), done = Even
7272
end
7373
end
7474

75-
let cmd = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no threads_exec.jl`
76-
for test_nthreads in (1, 2, 4, 4) # run once to try single-threaded mode, then try a couple times to trigger bad races
75+
76+
let cmd1 = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no threads_exec.jl`,
77+
cmd2 = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no -e 'print(Threads.threadpoolsize(:default), ",", Threads.threadpoolsize(:interactive))'`
78+
for (test_nthreads, test_nthreadsi) in (
79+
(1, 0),
80+
(1, 1),
81+
(2, 0),
82+
(2, 1),
83+
(4, 0),
84+
(4, 0)) # try a couple times to trigger bad races
7785
new_env = copy(ENV)
78-
new_env["JULIA_NUM_THREADS"] = string(test_nthreads)
79-
run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr))
86+
new_env["JULIA_NUM_THREADS"] = string(test_nthreads, ",", test_nthreadsi)
87+
run(pipeline(setenv(cmd1, new_env), stdout = stdout, stderr = stderr))
88+
threads_config = "$test_nthreads,$test_nthreadsi"
89+
# threads set via env var
90+
@test chomp(read(setenv(cmd2, new_env), String)) == threads_config
91+
# threads set via -t
92+
@test chomp(read(`$cmd2 -t$test_nthreads,$test_nthreadsi`, String)) == threads_config
8093
end
8194
end
8295

test/threads_exec.jl

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ Timer(t -> killjob("KILLING BY THREAD TEST WATCHDOG\n"), 1200)
3131
@testset """threads_exec.jl with JULIA_NUM_THREADS == $(ENV["JULIA_NUM_THREADS"])""" begin
3232

3333
@test Threads.threadid() == 1
34-
@test 1 <= threadpoolsize() <= Threads.maxthreadid()
34+
@test threadpool() in (:interactive, :default) # thread 1 could be in the interactive pool
35+
@test 1 <= threadpoolsize(:default) <= Threads.maxthreadid()
3536

3637
# basic lock check
37-
if threadpoolsize() > 1
38+
if threadpoolsize(:default) > 1
3839
let lk = SpinLock()
3940
c1 = Base.Event()
4041
c2 = Base.Event()
@@ -56,7 +57,18 @@ end
5657

5758
# threading constructs
5859

59-
let a = zeros(Int, 2 * threadpoolsize())
60+
@testset "@threads and @spawn threadpools" begin
61+
@threads for i in 1:1
62+
@test threadpool() == :default
63+
end
64+
@test fetch(Threads.@spawn threadpool()) == :default
65+
@test fetch(Threads.@spawn :default threadpool()) == :default
66+
if threadpoolsize(:interactive) > 0
67+
@test fetch(Threads.@spawn :interactive threadpool()) == :interactive
68+
end
69+
end
70+
71+
let a = zeros(Int, 2 * threadpoolsize(:default))
6072
@threads for i = 1:length(a)
6173
@sync begin
6274
@async begin
@@ -76,7 +88,7 @@ end
7688

7789
# parallel loop with parallel atomic addition
7890
function threaded_loop(a, r, x)
79-
counter = Threads.Atomic{Int}(min(threadpoolsize(), length(r)))
91+
counter = Threads.Atomic{Int}(min(threadpoolsize(:default), length(r)))
8092
@threads for i in r
8193
# synchronize the start given that each partition is started sequentially,
8294
# meaning that without the wait, if the loop is too fast the iteration can happen in order
@@ -429,7 +441,7 @@ end
429441
for T in intersect((Int32, Int64, Float32, Float64), Base.Threads.atomictypes)
430442
var = Atomic{T}()
431443
nloops = 1000
432-
di = threadpoolsize()
444+
di = threadpoolsize(:default)
433445
@threads for i in 1:di
434446
test_atomic_cas!(var, i:di:nloops)
435447
end
@@ -519,13 +531,13 @@ function test_thread_cfunction()
519531
@test cfs[1] == cf1
520532
@test cfs[2] == cf(fs[2])
521533
@test length(unique(cfs)) == 1000
522-
ok = zeros(Int, threadpoolsize())
534+
ok = zeros(Int, threadpoolsize(:default))
523535
@threads :static for i in 1:10000
524536
i = mod1(i, 1000)
525537
fi = fs[i]
526538
cfi = cf(fi)
527539
GC.@preserve cfi begin
528-
ok[threadid()] += (cfi === cfs[i])
540+
ok[threadid() - threadpoolsize(:interactive)] += (cfi === cfs[i])
529541
end
530542
end
531543
@test sum(ok) == 10000
@@ -582,17 +594,17 @@ test_nested_loops()
582594

583595
function test_thread_too_few_iters()
584596
x = Atomic()
585-
a = zeros(Int, threadpoolsize()+2)
586-
threaded_loop(a, 1:threadpoolsize()-1, x)
587-
found = zeros(Bool, threadpoolsize()+2)
588-
for i=1:threadpoolsize()-1
597+
a = zeros(Int, threadpoolsize(:default)+2)
598+
threaded_loop(a, 1:threadpoolsize(:default)-1, x)
599+
found = zeros(Bool, threadpoolsize(:default)+2)
600+
for i=1:threadpoolsize(:default)-1
589601
found[a[i]] = true
590602
end
591-
@test x[] == threadpoolsize()-1
603+
@test x[] == threadpoolsize(:default)-1
592604
# Next test checks that all loop iterations ran,
593605
# and were unique (via pigeon-hole principle).
594-
@test !(false in found[1:threadpoolsize()-1])
595-
@test !(true in found[threadpoolsize():end])
606+
@test !(false in found[1:threadpoolsize(:default)-1])
607+
@test !(true in found[threadpoolsize(:default):end])
596608
end
597609
test_thread_too_few_iters()
598610

@@ -734,10 +746,10 @@ function _atthreads_with_error(a, err)
734746
end
735747
a
736748
end
737-
@test_throws CompositeException _atthreads_with_error(zeros(threadpoolsize()), true)
738-
let a = zeros(threadpoolsize())
749+
@test_throws CompositeException _atthreads_with_error(zeros(threadpoolsize(:default)), true)
750+
let a = zeros(threadpoolsize(:default))
739751
_atthreads_with_error(a, false)
740-
@test a == [1:threadpoolsize();]
752+
@test a == [threadpoolsize(:interactive) .+ (1:threadpoolsize(:default));]
741753
end
742754

743755
# static schedule
@@ -748,11 +760,11 @@ function _atthreads_static_schedule(n)
748760
end
749761
return ids
750762
end
751-
@test _atthreads_static_schedule(threadpoolsize()) == 1:threadpoolsize()
752-
@test _atthreads_static_schedule(1) == [1;]
763+
@test _atthreads_static_schedule(threadpoolsize(:default)) == threadpoolsize(:interactive) .+ (1:threadpoolsize(:default))
764+
@test _atthreads_static_schedule(1) == [threadpoolsize(:interactive) + 1;]
753765
@test_throws(
754766
"`@threads :static` cannot be used concurrently or nested",
755-
@threads(for i = 1:1; _atthreads_static_schedule(threadpoolsize()); end),
767+
@threads(for i = 1:1; _atthreads_static_schedule(threadpoolsize(:default)); end),
756768
)
757769

758770
# dynamic schedule
@@ -765,35 +777,35 @@ function _atthreads_dynamic_schedule(n)
765777
end
766778
return inc[], flags
767779
end
768-
@test _atthreads_dynamic_schedule(threadpoolsize()) == (threadpoolsize(), ones(threadpoolsize()))
780+
@test _atthreads_dynamic_schedule(threadpoolsize(:default)) == (threadpoolsize(:default), ones(threadpoolsize(:default)))
769781
@test _atthreads_dynamic_schedule(1) == (1, ones(1))
770782
@test _atthreads_dynamic_schedule(10) == (10, ones(10))
771-
@test _atthreads_dynamic_schedule(threadpoolsize() * 2) == (threadpoolsize() * 2, ones(threadpoolsize() * 2))
783+
@test _atthreads_dynamic_schedule(threadpoolsize(:default) * 2) == (threadpoolsize(:default) * 2, ones(threadpoolsize(:default) * 2))
772784

773785
# nested dynamic schedule
774786
function _atthreads_dynamic_dynamic_schedule()
775787
inc = Threads.Atomic{Int}(0)
776-
Threads.@threads :dynamic for _ = 1:threadpoolsize()
777-
Threads.@threads :dynamic for _ = 1:threadpoolsize()
788+
Threads.@threads :dynamic for _ = 1:threadpoolsize(:default)
789+
Threads.@threads :dynamic for _ = 1:threadpoolsize(:default)
778790
Threads.atomic_add!(inc, 1)
779791
end
780792
end
781793
return inc[]
782794
end
783-
@test _atthreads_dynamic_dynamic_schedule() == threadpoolsize() * threadpoolsize()
795+
@test _atthreads_dynamic_dynamic_schedule() == threadpoolsize(:default) * threadpoolsize(:default)
784796

785797
function _atthreads_static_dynamic_schedule()
786-
ids = zeros(Int, threadpoolsize())
798+
ids = zeros(Int, threadpoolsize(:default))
787799
inc = Threads.Atomic{Int}(0)
788-
Threads.@threads :static for i = 1:threadpoolsize()
800+
Threads.@threads :static for i = 1:threadpoolsize(:default)
789801
ids[i] = Threads.threadid()
790-
Threads.@threads :dynamic for _ = 1:threadpoolsize()
802+
Threads.@threads :dynamic for _ = 1:threadpoolsize(:default)
791803
Threads.atomic_add!(inc, 1)
792804
end
793805
end
794806
return ids, inc[]
795807
end
796-
@test _atthreads_static_dynamic_schedule() == (1:threadpoolsize(), threadpoolsize() * threadpoolsize())
808+
@test _atthreads_static_dynamic_schedule() == (threadpoolsize(:interactive) .+ (1:threadpoolsize(:default)), threadpoolsize(:default) * threadpoolsize(:default))
797809

798810
# errors inside @threads :dynamic
799811
function _atthreads_dynamic_with_error(a)
@@ -802,7 +814,7 @@ function _atthreads_dynamic_with_error(a)
802814
end
803815
a
804816
end
805-
@test_throws "user error in the loop body" _atthreads_dynamic_with_error(zeros(threadpoolsize()))
817+
@test_throws "user error in the loop body" _atthreads_dynamic_with_error(zeros(threadpoolsize(:default)))
806818

807819
####
808820
# :greedy
@@ -817,57 +829,57 @@ function _atthreads_greedy_schedule(n)
817829
end
818830
return inc[], flags
819831
end
820-
@test _atthreads_greedy_schedule(threadpoolsize()) == (threadpoolsize(), ones(threadpoolsize()))
832+
@test _atthreads_greedy_schedule(threadpoolsize(:default)) == (threadpoolsize(:default), ones(threadpoolsize(:default)))
821833
@test _atthreads_greedy_schedule(1) == (1, ones(1))
822834
@test _atthreads_greedy_schedule(10) == (10, ones(10))
823-
@test _atthreads_greedy_schedule(threadpoolsize() * 2) == (threadpoolsize() * 2, ones(threadpoolsize() * 2))
835+
@test _atthreads_greedy_schedule(threadpoolsize(:default) * 2) == (threadpoolsize(:default) * 2, ones(threadpoolsize(:default) * 2))
824836

825837
# nested greedy schedule
826838
function _atthreads_greedy_greedy_schedule()
827839
inc = Threads.Atomic{Int}(0)
828-
Threads.@threads :greedy for _ = 1:threadpoolsize()
829-
Threads.@threads :greedy for _ = 1:threadpoolsize()
840+
Threads.@threads :greedy for _ = 1:threadpoolsize(:default)
841+
Threads.@threads :greedy for _ = 1:threadpoolsize(:default)
830842
Threads.atomic_add!(inc, 1)
831843
end
832844
end
833845
return inc[]
834846
end
835-
@test _atthreads_greedy_greedy_schedule() == threadpoolsize() * threadpoolsize()
847+
@test _atthreads_greedy_greedy_schedule() == threadpoolsize(:default) * threadpoolsize(:default)
836848

837849
function _atthreads_greedy_dynamic_schedule()
838850
inc = Threads.Atomic{Int}(0)
839-
Threads.@threads :greedy for _ = 1:threadpoolsize()
840-
Threads.@threads :dynamic for _ = 1:threadpoolsize()
851+
Threads.@threads :greedy for _ = 1:threadpoolsize(:default)
852+
Threads.@threads :dynamic for _ = 1:threadpoolsize(:default)
841853
Threads.atomic_add!(inc, 1)
842854
end
843855
end
844856
return inc[]
845857
end
846-
@test _atthreads_greedy_dynamic_schedule() == threadpoolsize() * threadpoolsize()
858+
@test _atthreads_greedy_dynamic_schedule() == threadpoolsize(:default) * threadpoolsize(:default)
847859

848860
function _atthreads_dymamic_greedy_schedule()
849861
inc = Threads.Atomic{Int}(0)
850-
Threads.@threads :dynamic for _ = 1:threadpoolsize()
851-
Threads.@threads :greedy for _ = 1:threadpoolsize()
862+
Threads.@threads :dynamic for _ = 1:threadpoolsize(:default)
863+
Threads.@threads :greedy for _ = 1:threadpoolsize(:default)
852864
Threads.atomic_add!(inc, 1)
853865
end
854866
end
855867
return inc[]
856868
end
857-
@test _atthreads_dymamic_greedy_schedule() == threadpoolsize() * threadpoolsize()
869+
@test _atthreads_dymamic_greedy_schedule() == threadpoolsize(:default) * threadpoolsize(:default)
858870

859871
function _atthreads_static_greedy_schedule()
860-
ids = zeros(Int, threadpoolsize())
872+
ids = zeros(Int, threadpoolsize(:default))
861873
inc = Threads.Atomic{Int}(0)
862-
Threads.@threads :static for i = 1:threadpoolsize()
874+
Threads.@threads :static for i = 1:threadpoolsize(:default)
863875
ids[i] = Threads.threadid()
864-
Threads.@threads :greedy for _ = 1:threadpoolsize()
876+
Threads.@threads :greedy for _ = 1:threadpoolsize(:default)
865877
Threads.atomic_add!(inc, 1)
866878
end
867879
end
868880
return ids, inc[]
869881
end
870-
@test _atthreads_static_greedy_schedule() == (1:threadpoolsize(), threadpoolsize() * threadpoolsize())
882+
@test _atthreads_static_greedy_schedule() == (threadpoolsize(:interactive) .+ (1:threadpoolsize(:default)), threadpoolsize(:default) * threadpoolsize(:default))
871883

872884
# errors inside @threads :greedy
873885
function _atthreads_greedy_with_error(a)
@@ -876,7 +888,7 @@ function _atthreads_greedy_with_error(a)
876888
end
877889
a
878890
end
879-
@test_throws "user error in the loop body" _atthreads_greedy_with_error(zeros(threadpoolsize()))
891+
@test_throws "user error in the loop body" _atthreads_greedy_with_error(zeros(threadpoolsize(:default)))
880892

881893
####
882894
# multi-argument loop
@@ -1109,7 +1121,7 @@ function check_sync_end_race()
11091121
nnotscheduled += y === :notscheduled
11101122
end
11111123
# Useful for tuning the test:
1112-
@debug "`check_sync_end_race` done" threadpoolsize() ncompleted nnotscheduled nerror
1124+
@debug "`check_sync_end_race` done" threadpoolsize(:default) ncompleted nnotscheduled nerror
11131125
finally
11141126
done[] = true
11151127
end
@@ -1123,7 +1135,7 @@ end
11231135

11241136
# issue #41546, thread-safe package loading
11251137
@testset "package loading" begin
1126-
ntasks = max(threadpoolsize(), 4)
1138+
ntasks = max(threadpoolsize(:default), 4)
11271139
ch = Channel{Bool}(ntasks)
11281140
barrier = Base.Event()
11291141
old_act_proj = Base.ACTIVE_PROJECT[]

0 commit comments

Comments
 (0)