|
1 | 1 | struct ThreadTask
|
2 |
| - p::Ptr{UInt} |
| 2 | + p::Ptr{UInt} |
3 | 3 | end
|
4 | 4 | Base.pointer(tt::ThreadTask) = tt.p
|
5 | 5 |
|
6 | 6 | @inline taskpointer(tid::T) where {T} = THREADPOOLPTR[] + tid*(THREADBUFFERSIZE%T)
|
7 | 7 |
|
8 | 8 | @inline function _call(p::Ptr{UInt})
|
9 |
| - fptr = load(p + sizeof(UInt), Ptr{Cvoid}) |
10 |
| - assume(fptr ≠ C_NULL) |
11 |
| - ccall(fptr, Cvoid, (Ptr{UInt},), p) |
| 9 | + fptr = load(p + sizeof(UInt), Ptr{Cvoid}) |
| 10 | + assume(fptr ≠ C_NULL) |
| 11 | + ccall(fptr, Cvoid, (Ptr{UInt},), p) |
12 | 12 | end
|
13 | 13 | @inline function launch(f::F, tid::Integer, args::Vararg{Any,K}) where {F,K}
|
14 |
| - p = taskpointer(tid) |
15 |
| - f(p, args...) |
16 |
| - state = _atomic_xchg!(p, TASK) |
17 |
| - state == WAIT && wake_thread!(tid) |
18 |
| - return nothing |
| 14 | + p = taskpointer(tid) |
| 15 | + f(p, args...) |
| 16 | + state = _atomic_xchg!(p, TASK) # exchange must happen atomically, to prevent it from switching to `WAIT` after reading |
| 17 | + state == WAIT && wake_thread!(tid) |
| 18 | + return nothing |
19 | 19 | end
|
20 | 20 |
|
21 | 21 | function (tt::ThreadTask)()
|
22 |
| - p = pointer(tt) |
23 |
| - max_wait = one(UInt32) << 20 |
24 |
| - wait_counter = max_wait |
25 |
| - GC.@preserve THREADPOOL begin |
26 |
| - while true |
27 |
| - # if _atomic_state(p) == TASK |
28 |
| - if _atomic_cas_cmp!(p, TASK, EXEC) |
29 |
| - _call(p) |
30 |
| - # store!(p, SPIN) |
31 |
| - _atomic_store!(p, SPIN) |
32 |
| - wait_counter = zero(UInt32) |
33 |
| - continue |
34 |
| - end |
35 |
| - pause() |
36 |
| - if (wait_counter += one(UInt32)) > max_wait |
37 |
| - wait_counter = zero(UInt32) |
38 |
| - _atomic_cas_cmp!(p, SPIN, WAIT) && Base.wait() |
39 |
| - end |
40 |
| - end |
| 22 | + p = pointer(tt) |
| 23 | + max_wait = one(UInt32) << 20 |
| 24 | + wait_counter = max_wait |
| 25 | + GC.@preserve THREADPOOL begin |
| 26 | + while true |
| 27 | + if _atomic_state(p) == TASK |
| 28 | + # if _atomic_cas_cmp!(p, TASK, EXEC) |
| 29 | + _call(p) |
| 30 | + # store!(p, SPIN) |
| 31 | + _atomic_store!(p, SPIN) |
| 32 | + wait_counter = zero(UInt32) |
| 33 | + continue |
| 34 | + end |
| 35 | + pause() |
| 36 | + if (wait_counter += one(UInt32)) > max_wait |
| 37 | + wait_counter = zero(UInt32) |
| 38 | + _atomic_cas_cmp!(p, SPIN, WAIT) && Base.wait() |
| 39 | + end |
41 | 40 | end
|
| 41 | + end |
42 | 42 | end
|
43 | 43 |
|
44 | 44 | # 1-based tid, pushes into task 2-nthreads()
|
45 | 45 | # function wake_thread!(tid::T) where {T <: Unsigned}
|
46 | 46 | @noinline function wake_thread!(_tid::T) where {T <: Integer}
|
47 | 47 | tid = _tid % Int
|
48 |
| - store!(taskpointer(_tid), TASK) |
| 48 | + # store!(taskpointer(_tid), TASK) |
49 | 49 | tidp1 = tid + one(tid)
|
50 | 50 | assume(unsigned(length(Base.Workqueues)) > unsigned(tid))
|
51 | 51 | assume(unsigned(length(TASKS)) > unsigned(tidp1))
|
|
56 | 56 | # 1-based tid
|
57 | 57 | @inline wait(tid::Integer) = wait(taskpointer(tid))
|
58 | 58 | @inline function wait(p::Ptr{UInt})
|
59 |
| - # TASK = 0 |
60 |
| - # EXEC = 1 |
61 |
| - # WAIT = 2 |
62 |
| - # SPIN = 3 |
63 |
| - s = _atomic_umax!(p, EXEC) # s = old state, state gets set to EXEC if s == TASK or s == EXEC |
64 |
| - if s == TASK # thread hasn't begun yet for some reason, so we steal the work. |
65 |
| - _call(p) |
66 |
| - store!(p, SPIN) |
67 |
| - return |
68 |
| - end |
69 | 59 | counter = 0x00000000
|
70 |
| - while _atomic_state(p) == EXEC |
| 60 | + while _atomic_state(p) == TASK |
71 | 61 | pause()
|
72 | 62 | ((counter += 0x00000001) > 0x00010000) && yield()
|
73 | 63 | end
|
74 | 64 | end
|
75 | 65 |
|
76 |
| - |
77 |
| -# function launch_thread(f::F, tid) where {F} |
78 |
| -# cfunc = @cfunction($mapper, Cvoid, ()); |
79 |
| - |
80 |
| -# fptr = Base.unsafe_convert(Ptr{Cvoid}, cfunc) |
81 |
| - |
82 |
| -# ccall(fptr, Cvoid, ()) |
83 |
| - |
84 |
| -# end |
0 commit comments