@@ -15,11 +15,12 @@ mutable struct AsyncCondition
1515 handle:: Ptr{Cvoid}
1616 cond:: ThreadSynchronizer
1717 isopen:: Bool
18+ set:: Bool
1819
1920 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 ()
2123 associate_julia_struct (this. handle, this)
22- finalizer (uvfinalize, this)
2324 err = ccall (:uv_async_init , Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
2425 eventloop (), this, uv_jl_asynccb:: Ptr{Cvoid} )
2526 if err != 0
@@ -28,6 +29,8 @@ mutable struct AsyncCondition
2829 this. handle = C_NULL
2930 throw (_UVError (" uv_async_init" , err))
3031 end
32+ finalizer (uvfinalize, this)
33+ iolock_end ()
3134 return this
3235 end
3336end
@@ -40,28 +43,10 @@ the async condition object itself.
4043"""
4144function AsyncCondition (cb:: Function )
4245 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
6049 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)
6550 return async
6651end
6752
@@ -81,59 +66,108 @@ mutable struct Timer
8166 handle:: Ptr{Cvoid}
8267 cond:: ThreadSynchronizer
8368 isopen:: Bool
69+ set:: Bool
8470
8571 function Timer (timeout:: Real ; interval:: Real = 0.0 )
8672 timeout ≥ 0 || throw (ArgumentError (" timer cannot have negative timeout of $timeout seconds" ))
8773 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 ()
8877
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
9483 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 ()
9589 return this
9690 end
9791end
9892
9993unsafe_convert (:: Type{Ptr{Cvoid}} , t:: Timer ) = t. handle
10094unsafe_convert (:: Type{Ptr{Cvoid}} , async:: AsyncCondition ) = async. handle
10195
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 ()
109122 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
110130end
111131
132+
112133isopen (t:: Union{Timer, AsyncCondition} ) = t. isopen
113134
114135function close (t:: Union{Timer, AsyncCondition} )
136+ iolock_begin ()
115137 if t. handle != C_NULL && isopen (t)
116138 t. isopen = false
117139 ccall (:jl_close_uv , Cvoid, (Ptr{Cvoid},), t)
118140 end
141+ iolock_end ()
119142 nothing
120143end
121144
122145function 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)
127160 end
128- t . isopen = false
161+ iolock_end ()
129162 nothing
130163end
131164
132165function _uv_hook_close (t:: Union{Timer, AsyncCondition} )
133166 lock (t. cond)
134167 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)
137171 finally
138172 unlock (t. cond)
139173 end
@@ -144,7 +178,8 @@ function uv_asynccb(handle::Ptr{Cvoid})
144178 async = @handle_as handle AsyncCondition
145179 lock (async. cond)
146180 try
147- notify (async. cond)
181+ async. set = true
182+ notify (async. cond, true )
148183 finally
149184 unlock (async. cond)
150185 end
@@ -155,11 +190,12 @@ function uv_timercb(handle::Ptr{Cvoid})
155190 t = @handle_as handle Timer
156191 lock (t. cond)
157192 try
193+ t. set = true
158194 if ccall (:uv_timer_get_repeat , UInt64, (Ptr{Cvoid},), t) == 0
159195 # timer is stopped now
160196 close (t)
161197 end
162- notify (t. cond)
198+ notify (t. cond, true )
163199 finally
164200 unlock (t. cond)
165201 end
@@ -199,7 +235,7 @@ Here the first number is printed after a delay of two seconds, then the followin
199235julia> begin
200236 i = 0
201237 cb(timer) = (global i += 1; println(i))
202- t = Timer(cb, 2, interval = 0.2)
238+ t = Timer(cb, 2, interval= 0.2)
203239 wait(t)
204240 sleep(0.5)
205241 close(t)
@@ -209,37 +245,28 @@ julia> begin
2092453
210246```
211247"""
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
224253 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
230255end
231256
232257"""
233258 timedwait(testcb::Function, secs::Float64; pollint::Float64=0.1)
234259
235260Waits until `testcb` returns `true` or for `secs` seconds, whichever is earlier.
236261`testcb` is polled every `pollint` seconds.
262+
263+ Returns :ok, :timed_out, or :error
237264"""
238265function timedwait (testcb:: Function , secs:: Float64 ; pollint:: Float64 = 0.1 )
239266 pollint > 0 || throw (ArgumentError (" cannot set pollint to $pollint seconds" ))
240267 start = time ()
241268 done = Channel (1 )
242- timercb (aw) = begin
269+ function timercb (aw)
243270 try
244271 if testcb ()
245272 put! (done, :ok )
@@ -251,14 +278,15 @@ function timedwait(testcb::Function, secs::Float64; pollint::Float64=0.1)
251278 finally
252279 isready (done) && close (aw)
253280 end
281+ nothing
254282 end
255283
256284 if ! testcb ()
257285 t = Timer (timercb, pollint, interval = pollint)
258- ret = fetch (done)
286+ ret = fetch (done):: Symbol
259287 close (t)
260288 else
261289 ret = :ok
262290 end
263- ret
291+ return ret
264292end
0 commit comments