@@ -15,11 +15,12 @@ mutable struct AsyncCondition
15
15
handle:: Ptr{Cvoid}
16
16
cond:: ThreadSynchronizer
17
17
isopen:: Bool
18
+ set:: Bool
18
19
19
20
function AsyncCondition ()
20
- this = new (Libc. malloc (_sizeof_uv_async), ThreadSynchronizer (), true )
21
+ this = new (Libc. malloc (_sizeof_uv_async), ThreadSynchronizer (), true , false )
22
+ iolock_begin ()
21
23
associate_julia_struct (this. handle, this)
22
- finalizer (uvfinalize, this)
23
24
err = ccall (:uv_async_init , Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
24
25
eventloop (), this, uv_jl_asynccb:: Ptr{Cvoid} )
25
26
if err != 0
@@ -28,6 +29,8 @@ mutable struct AsyncCondition
28
29
this. handle = C_NULL
29
30
throw (_UVError (" uv_async_init" , err))
30
31
end
32
+ finalizer (uvfinalize, this)
33
+ iolock_end ()
31
34
return this
32
35
end
33
36
end
@@ -40,28 +43,10 @@ the async condition object itself.
40
43
"""
41
44
function AsyncCondition (cb:: Function )
42
45
async = AsyncCondition ()
43
- waiter = Task (function ()
44
- lock (async. cond)
45
- try
46
- while isopen (async)
47
- success = try
48
- stream_wait (async, async. cond)
49
- true
50
- catch exc # ignore possible exception on close()
51
- isa (exc, EOFError) || rethrow ()
52
- finally
53
- unlock (async. cond)
54
- end
55
- success && cb (async)
56
- lock (async. cond)
57
- end
58
- finally
59
- unlock (async. cond)
46
+ @async while _trywait (async)
47
+ cb (async)
48
+ isopen (async) || return
60
49
end
61
- end )
62
- # must start the task right away so that it can wait for the AsyncCondition before
63
- # we re-enter the event loop. this avoids a race condition. see issue #12719
64
- yield (waiter)
65
50
return async
66
51
end
67
52
@@ -81,59 +66,108 @@ mutable struct Timer
81
66
handle:: Ptr{Cvoid}
82
67
cond:: ThreadSynchronizer
83
68
isopen:: Bool
69
+ set:: Bool
84
70
85
71
function Timer (timeout:: Real ; interval:: Real = 0.0 )
86
72
timeout ≥ 0 || throw (ArgumentError (" timer cannot have negative timeout of $timeout seconds" ))
87
73
interval ≥ 0 || throw (ArgumentError (" timer cannot have negative repeat interval of $interval seconds" ))
74
+ timeout = UInt64 (round (timeout * 1000 )) + 1
75
+ interval = UInt64 (round (interval * 1000 ))
76
+ loop = eventloop ()
88
77
89
- this = new (Libc. malloc (_sizeof_uv_timer), ThreadSynchronizer (), true )
90
- ccall ( :jl_uv_update_timer_start , Cvoid,
91
- (Ptr{Cvoid}, Any, Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64),
92
- eventloop (), this, this . handle, uv_jl_timercb :: Ptr{Cvoid} ,
93
- UInt64 ( round (timeout * 1000 )) + 1 , UInt64 ( round (interval * 1000 )))
78
+ this = new (Libc. malloc (_sizeof_uv_timer), ThreadSynchronizer (), true , false )
79
+ associate_julia_struct (this . handle, this)
80
+ iolock_begin ()
81
+ err = ccall ( :uv_timer_init , Cint, (Ptr{Cvoid}, Ptr{Cvoid}), loop, this)
82
+ @assert err == 0
94
83
finalizer (uvfinalize, this)
84
+ ccall (:uv_update_time , Cvoid, (Ptr{Cvoid},), loop)
85
+ err = ccall (:uv_timer_start , Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64),
86
+ this, uv_jl_timercb:: Ptr{Cvoid} , timeout, interval)
87
+ @assert err == 0
88
+ iolock_end ()
95
89
return this
96
90
end
97
91
end
98
92
99
93
unsafe_convert (:: Type{Ptr{Cvoid}} , t:: Timer ) = t. handle
100
94
unsafe_convert (:: Type{Ptr{Cvoid}} , async:: AsyncCondition ) = async. handle
101
95
102
- function wait (t:: Union{Timer, AsyncCondition} )
103
- lock (t. cond)
104
- try
105
- isopen (t) || throw (EOFError ())
106
- stream_wait (t, t. cond)
107
- finally
108
- unlock (t. cond)
96
+ function _trywait (t:: Union{Timer, AsyncCondition} )
97
+ set = t. set
98
+ if ! set
99
+ t. handle == C_NULL && return false
100
+ iolock_begin ()
101
+ set = t. set
102
+ if ! set
103
+ preserve_handle (t)
104
+ lock (t. cond)
105
+ try
106
+ set = t. set
107
+ if ! set
108
+ if t. handle != C_NULL
109
+ iolock_end ()
110
+ set = wait (t. cond)
111
+ unlock (t. cond)
112
+ iolock_begin ()
113
+ lock (t. cond)
114
+ end
115
+ end
116
+ finally
117
+ unlock (t. cond)
118
+ unpreserve_handle (t)
119
+ end
120
+ end
121
+ iolock_end ()
109
122
end
123
+ t. set = false
124
+ return set
125
+ end
126
+
127
+ function wait (t:: Union{Timer, AsyncCondition} )
128
+ _trywait (t) || throw (EOFError ())
129
+ nothing
110
130
end
111
131
132
+
112
133
isopen (t:: Union{Timer, AsyncCondition} ) = t. isopen
113
134
114
135
function close (t:: Union{Timer, AsyncCondition} )
136
+ iolock_begin ()
115
137
if t. handle != C_NULL && isopen (t)
116
138
t. isopen = false
117
139
ccall (:jl_close_uv , Cvoid, (Ptr{Cvoid},), t)
118
140
end
141
+ iolock_end ()
119
142
nothing
120
143
end
121
144
122
145
function uvfinalize (t:: Union{Timer, AsyncCondition} )
123
- if t. handle != C_NULL
124
- disassociate_julia_struct (t. handle) # not going to call the usual close hooks
125
- close (t)
126
- t. handle = C_NULL
146
+ iolock_begin ()
147
+ lock (t. cond)
148
+ try
149
+ if t. handle != C_NULL
150
+ disassociate_julia_struct (t. handle) # not going to call the usual close hooks
151
+ if t. isopen
152
+ t. isopen = false
153
+ ccall (:jl_close_uv , Cvoid, (Ptr{Cvoid},), t)
154
+ end
155
+ t. handle = C_NULL
156
+ notify (t. cond, false )
157
+ end
158
+ finally
159
+ unlock (t. cond)
127
160
end
128
- t . isopen = false
161
+ iolock_end ()
129
162
nothing
130
163
end
131
164
132
165
function _uv_hook_close (t:: Union{Timer, AsyncCondition} )
133
166
lock (t. cond)
134
167
try
135
- uvfinalize (t)
136
- notify_error (t. cond, EOFError ())
168
+ t. isopen = false
169
+ t. handle = C_NULL
170
+ notify (t. cond, t. set)
137
171
finally
138
172
unlock (t. cond)
139
173
end
@@ -144,7 +178,8 @@ function uv_asynccb(handle::Ptr{Cvoid})
144
178
async = @handle_as handle AsyncCondition
145
179
lock (async. cond)
146
180
try
147
- notify (async. cond)
181
+ async. set = true
182
+ notify (async. cond, true )
148
183
finally
149
184
unlock (async. cond)
150
185
end
@@ -155,11 +190,12 @@ function uv_timercb(handle::Ptr{Cvoid})
155
190
t = @handle_as handle Timer
156
191
lock (t. cond)
157
192
try
193
+ t. set = true
158
194
if ccall (:uv_timer_get_repeat , UInt64, (Ptr{Cvoid},), t) == 0
159
195
# timer is stopped now
160
196
close (t)
161
197
end
162
- notify (t. cond)
198
+ notify (t. cond, true )
163
199
finally
164
200
unlock (t. cond)
165
201
end
@@ -199,7 +235,7 @@ Here the first number is printed after a delay of two seconds, then the followin
199
235
julia> begin
200
236
i = 0
201
237
cb(timer) = (global i += 1; println(i))
202
- t = Timer(cb, 2, interval = 0.2)
238
+ t = Timer(cb, 2, interval= 0.2)
203
239
wait(t)
204
240
sleep(0.5)
205
241
close(t)
@@ -209,37 +245,28 @@ julia> begin
209
245
3
210
246
```
211
247
"""
212
- function Timer (cb:: Function , timeout:: Real ; interval:: Real = 0.0 )
213
- t = Timer (timeout, interval = interval)
214
- waiter = Task (function ()
215
- while isopen (t)
216
- success = try
217
- wait (t)
218
- true
219
- catch exc # ignore possible exception on close()
220
- isa (exc, EOFError) || rethrow ()
221
- false
222
- end
223
- success && cb (t)
248
+ function Timer (cb:: Function , timeout:: Real ; interval:: Real = 0.0 )
249
+ timer = Timer (timeout, interval= interval)
250
+ @async while _trywait (timer)
251
+ cb (timer)
252
+ isopen (timer) || return
224
253
end
225
- end )
226
- # must start the task right away so that it can wait for the Timer before
227
- # we re-enter the event loop. this avoids a race condition. see issue #12719
228
- yield (waiter)
229
- return t
254
+ return timer
230
255
end
231
256
232
257
"""
233
258
timedwait(testcb::Function, secs::Float64; pollint::Float64=0.1)
234
259
235
260
Waits until `testcb` returns `true` or for `secs` seconds, whichever is earlier.
236
261
`testcb` is polled every `pollint` seconds.
262
+
263
+ Returns :ok, :timed_out, or :error
237
264
"""
238
265
function timedwait (testcb:: Function , secs:: Float64 ; pollint:: Float64 = 0.1 )
239
266
pollint > 0 || throw (ArgumentError (" cannot set pollint to $pollint seconds" ))
240
267
start = time ()
241
268
done = Channel (1 )
242
- timercb (aw) = begin
269
+ function timercb (aw)
243
270
try
244
271
if testcb ()
245
272
put! (done, :ok )
@@ -251,14 +278,15 @@ function timedwait(testcb::Function, secs::Float64; pollint::Float64=0.1)
251
278
finally
252
279
isready (done) && close (aw)
253
280
end
281
+ nothing
254
282
end
255
283
256
284
if ! testcb ()
257
285
t = Timer (timercb, pollint, interval = pollint)
258
- ret = fetch (done)
286
+ ret = fetch (done):: Symbol
259
287
close (t)
260
288
else
261
289
ret = :ok
262
290
end
263
- ret
291
+ return ret
264
292
end
0 commit comments