-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
Copy pathtask.jl
176 lines (153 loc) · 3.9 KB
/
task.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
show(io::IO, t::Task) = print(io, "Task")
current_task() = ccall(:jl_get_current_task, Any, ())::Task
istaskdone(t::Task) = t.done
istaskstarted(t::Task) = isdefined(t,:parent)
# yield to a task, throwing an exception in it
function throwto(t::Task, exc)
t.exception = exc
yieldto(t)
end
function task_local_storage()
t = current_task()
if is(t.storage, nothing)
t.storage = ObjectIdDict()
end
(t.storage)::ObjectIdDict
end
task_local_storage(key) = task_local_storage()[key]
task_local_storage(key, val) = (task_local_storage()[key] = val)
function task_local_storage(body::Function, key, val)
tls = task_local_storage()
hadkey = haskey(tls,key)
old = get(tls,key,nothing)
tls[key] = val
try body()
finally
hadkey ? (tls[key] = old) : delete!(tls,key)
end
end
# NOTE: you can only wait for scheduled tasks
function wait(t::Task)
if is(t.donenotify, nothing)
t.donenotify = Condition()
end
while !istaskdone(t)
wait(t.donenotify)
end
t.result
end
function produce(v)
ct = current_task()
q = ct.consumers
if isa(q,Condition)
# make a task waiting for us runnable again
notify1(q)
end
yieldto(ct.last, v)
ct.parent = ct.last # always exit to last consumer
nothing
end
produce(v...) = produce(v)
function consume(P::Task)
while !(P.runnable || P.done)
if P.consumers === nothing
P.consumers = Condition()
end
wait(P.consumers)
end
ct = current_task()
prev = ct.last
ct.runnable = false
v = yieldto(P)
ct.last = prev
ct.runnable = true
if P.done
q = P.consumers
if !is(q, nothing)
notify(q, P.result)
end
end
v
end
start(t::Task) = nothing
function done(t::Task, val)
t.result = consume(t)
istaskdone(t)
end
next(t::Task, val) = (t.result, nothing)
macro task(ex)
:(Task(()->$(esc(ex))))
end
# schedule an expression to run asynchronously, with minimal ceremony
macro schedule(expr)
expr = localize_vars(:(()->($expr)), false)
:(enq_work(Task($(esc(expr)))))
end
schedule(t::Task) = enq_work(t)
## condition variables
type Condition
waitq::Vector{Any}
Condition() = new({})
end
function wait(c::Condition)
ct = current_task()
if ct === Scheduler
error("cannot execute blocking function from scheduler")
end
push!(c.waitq, ct)
ct.runnable = false
try
yield(c)
catch
filter!(x->x!==ct, c.waitq)
rethrow()
end
end
function wait()
ct = current_task()
if ct === Scheduler
error("cannot execute blocking function from scheduler")
end
ct.runnable = false
yield()
end
function notify(t::Task, arg::ANY=nothing; error=false)
if t.runnable == true
Base.error("tried to resume task that is not stopped")
end
if error
t.exception = arg
else
t.result = arg
end
enq_work(t)
nothing
end
notify_error(t::Task, err) = notify(t, err, error=true)
function notify(c::Condition, arg::ANY=nothing; all=true, error=false)
if all
for t in c.waitq
!error ? (t.result = arg) : (t.exception = arg)
enq_work(t)
end
empty!(c.waitq)
elseif !isempty(c.waitq)
t = shift!(c.waitq)
!error? (t.result = arg) : (t.exception = arg)
enq_work(t)
end
nothing
end
notify1(c::Condition, arg=nothing) = notify(c, arg, all=false)
notify_error(c::Condition, err) = notify(c, err, error=true)
notify1_error(c::Condition, err) = notify(c, err, error=true, all=false)
function task_done_hook(t::Task)
if isa(t.donenotify, Condition)
if isdefined(t,:exception) && t.exception !== nothing
# TODO: maybe wrap this in a TaskExited exception
notify_error(t.donenotify, t.exception)
else
notify(t.donenotify, t.result)
end
end
end