Skip to content

Commit 69acc13

Browse files
Revert "Revert "stop using raw libuv API" (#156)"
This reverts commit c91876a.
1 parent c91876a commit 69acc13

File tree

4 files changed

+76
-146
lines changed

4 files changed

+76
-146
lines changed

Project.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "1.5.1"
55

66
[deps]
77
ArgTools = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
8+
FileWatching = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
89
LibCURL = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
910
NetworkOptions = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
1011

src/Curl/Curl.jl

+25-2
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,38 @@ export
2626
remove_handle
2727

2828
using LibCURL
29-
using LibCURL: curl_off_t
29+
using LibCURL: curl_off_t, libcurl
3030
# not exported: https://github.com/JuliaWeb/LibCURL.jl/issues/87
3131

3232
# constants that LibCURL should have but doesn't
3333
const CURLE_PEER_FAILED_VERIFICATION = 60
3434
const CURLSSLOPT_REVOKE_BEST_EFFORT = 1 << 3
3535

36+
# these are incorrectly defined on Windows by LibCURL:
37+
if Sys.iswindows()
38+
const curl_socket_t = Base.OS_HANDLE
39+
const CURL_SOCKET_TIMEOUT = Base.INVALID_OS_HANDLE
40+
else
41+
const curl_socket_t = Cint
42+
const CURL_SOCKET_TIMEOUT = -1
43+
end
44+
45+
# definitions affected by incorrect curl_socket_t (copied verbatim):
46+
function curl_multi_socket_action(multi_handle, s, ev_bitmask, running_handles)
47+
ccall((:curl_multi_socket_action, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Cint, Ptr{Cint}), multi_handle, s, ev_bitmask, running_handles)
48+
end
49+
function curl_multi_assign(multi_handle, sockfd, sockp)
50+
ccall((:curl_multi_assign, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Ptr{Cvoid}), multi_handle, sockfd, sockp)
51+
end
52+
53+
# additional curl_multi_socket_action method
54+
function curl_multi_socket_action(multi_handle, s, ev_bitmask)
55+
curl_multi_socket_action(multi_handle, s, ev_bitmask, Ref{Cint}())
56+
end
57+
58+
using FileWatching
3659
using NetworkOptions
37-
using Base: preserve_handle, unpreserve_handle
60+
using Base: OS_HANDLE, preserve_handle, unpreserve_handle
3861

3962
include("utils.jl")
4063

src/Curl/Multi.jl

+45-77
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
mutable struct Multi
22
lock :: ReentrantLock
33
handle :: Ptr{Cvoid}
4-
timer :: Ptr{Cvoid}
4+
timer :: Timer
55
easies :: Vector{Easy}
66
grace :: UInt64
77

88
function Multi(grace::Integer = typemax(UInt64))
9-
timer = jl_malloc(Base._sizeof_uv_timer)
10-
uv_timer_init(timer)
11-
multi = new(ReentrantLock(), C_NULL, timer, Easy[], grace)
9+
multi = new(ReentrantLock(), C_NULL, Timer(0), Easy[], grace)
1210
finalizer(multi) do multi
13-
uv_timer_stop(multi.timer)
14-
uv_close(multi.timer, cglobal(:jl_free))
11+
close(multi.timer)
1512
done!(multi)
1613
end
1714
end
@@ -32,19 +29,11 @@ end
3229

3330
# adding & removing easy handles
3431

35-
function cleanup_callback(uv_timer_p::Ptr{Cvoid})::Cvoid
36-
## TODO: use a member access API
37-
multi_p = unsafe_load(convert(Ptr{Ptr{Cvoid}}, uv_timer_p))
38-
multi = unsafe_pointer_to_objref(multi_p)::Multi
39-
done!(multi)
40-
return
41-
end
42-
4332
function add_handle(multi::Multi, easy::Easy)
4433
lock(multi.lock) do
4534
if isempty(multi.easies)
4635
preserve_handle(multi)
47-
uv_timer_stop(multi.timer) # stop grace timer
36+
close(multi.timer) # stop grace timer
4837
end
4938
push!(multi.easies, easy)
5039
init!(multi)
@@ -57,11 +46,14 @@ function remove_handle(multi::Multi, easy::Easy)
5746
@check curl_multi_remove_handle(multi.handle, easy.handle)
5847
deleteat!(multi.easies, findlast(==(easy), multi.easies)::Int)
5948
!isempty(multi.easies) && return
60-
cleanup_cb = @cfunction(cleanup_callback, Cvoid, (Ptr{Cvoid},))
6149
if multi.grace <= 0
6250
done!(multi)
6351
elseif 0 < multi.grace < typemax(multi.grace)
64-
uv_timer_start(multi.timer, cleanup_cb, multi.grace, 0)
52+
multi.timer = Timer(multi.grace/1000)
53+
@async begin
54+
wait(multi.timer)
55+
isopen(multi.timer) && done!(multi)
56+
end
6557
end
6658
unpreserve_handle(multi)
6759
end
@@ -73,15 +65,14 @@ function set_defaults(multi::Multi)
7365
# currently no defaults
7466
end
7567

76-
# libuv callbacks
68+
# multi-socket handle state updates
7769

7870
struct CURLMsg
7971
msg :: CURLMSG
8072
easy :: Ptr{Cvoid}
8173
code :: CURLcode
8274
end
8375

84-
# should already be locked
8576
function check_multi_info(multi::Multi)
8677
while true
8778
p = curl_multi_info_read(multi.handle, Ref{Cint}())
@@ -104,37 +95,15 @@ function check_multi_info(multi::Multi)
10495
end
10596
end
10697

107-
function event_callback(
108-
uv_poll_p :: Ptr{Cvoid},
109-
status :: Cint,
110-
events :: Cint,
111-
)::Cvoid
112-
## TODO: use a member access API
113-
multi_p = unsafe_load(convert(Ptr{Ptr{Cvoid}}, uv_poll_p))
114-
multi = unsafe_pointer_to_objref(multi_p)::Multi
115-
sock_p = uv_poll_p + Base._sizeof_uv_poll
116-
sock = unsafe_load(convert(Ptr{curl_socket_t}, sock_p))
117-
flags = 0
118-
events & UV_READABLE != 0 && (flags |= CURL_CSELECT_IN)
119-
events & UV_WRITABLE != 0 && (flags |= CURL_CSELECT_OUT)
120-
lock(multi.lock) do
121-
@check curl_multi_socket_action(multi.handle, sock, flags)
122-
check_multi_info(multi)
123-
end
124-
end
98+
# curl callbacks
12599

126-
function timeout_callback(uv_timer_p::Ptr{Cvoid})::Cvoid
127-
## TODO: use a member access API
128-
multi_p = unsafe_load(convert(Ptr{Ptr{Cvoid}}, uv_timer_p))
129-
multi = unsafe_pointer_to_objref(multi_p)::Multi
100+
function do_multi(multi::Multi)
130101
lock(multi.lock) do
131102
@check curl_multi_socket_action(multi.handle, CURL_SOCKET_TIMEOUT, 0)
132103
check_multi_info(multi)
133104
end
134105
end
135106

136-
# curl callbacks
137-
138107
function timer_callback(
139108
multi_h :: Ptr{Cvoid},
140109
timeout_ms :: Clong,
@@ -143,15 +112,13 @@ function timer_callback(
143112
multi = unsafe_pointer_to_objref(multi_p)::Multi
144113
@assert multi_h == multi.handle
145114
if timeout_ms == 0
146-
lock(multi.lock) do
147-
@check curl_multi_socket_action(multi.handle, CURL_SOCKET_TIMEOUT, 0)
148-
check_multi_info(multi)
149-
end
115+
do_multi(multi)
150116
elseif timeout_ms >= 0
151-
timeout_cb = @cfunction(timeout_callback, Cvoid, (Ptr{Cvoid},))
152-
uv_timer_start(multi.timer, timeout_cb, max(1, timeout_ms), 0)
117+
multi.timer = Timer(timeout_ms/1000) do timer
118+
do_multi(multi)
119+
end
153120
elseif timeout_ms == -1
154-
uv_timer_stop(multi.timer)
121+
close(multi.timer)
155122
else
156123
@async @error("timer_callback: invalid timeout value", timeout_ms)
157124
return -1
@@ -164,46 +131,47 @@ function socket_callback(
164131
sock :: curl_socket_t,
165132
action :: Cint,
166133
multi_p :: Ptr{Cvoid},
167-
uv_poll_p :: Ptr{Cvoid},
134+
watcher_p :: Ptr{Cvoid},
168135
)::Cint
136+
if action (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT, CURL_POLL_REMOVE)
137+
@async @error("socket_callback: unexpected action", action)
138+
return -1
139+
end
169140
multi = unsafe_pointer_to_objref(multi_p)::Multi
141+
if watcher_p != C_NULL
142+
old_watcher = unsafe_pointer_to_objref(watcher_p)::FDWatcher
143+
@check curl_multi_assign(multi.handle, sock, C_NULL)
144+
unpreserve_handle(old_watcher)
145+
end
170146
if action in (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT)
171-
if uv_poll_p == C_NULL
172-
uv_poll_p = uv_poll_alloc()
173-
uv_poll_init(uv_poll_p, sock)
174-
## TODO: use a member access API
175-
unsafe_store!(convert(Ptr{Ptr{Cvoid}}, uv_poll_p), multi_p)
176-
sock_p = uv_poll_p + Base._sizeof_uv_poll
177-
unsafe_store!(convert(Ptr{curl_socket_t}, sock_p), sock)
178-
lock(multi.lock) do
179-
@check curl_multi_assign(multi.handle, sock, uv_poll_p)
147+
readable = action in (CURL_POLL_IN, CURL_POLL_INOUT)
148+
writable = action in (CURL_POLL_OUT, CURL_POLL_INOUT)
149+
watcher = FDWatcher(OS_HANDLE(sock), readable, writable)
150+
preserve_handle(watcher)
151+
watcher_p = pointer_from_objref(watcher)
152+
@check curl_multi_assign(multi.handle, sock, watcher_p)
153+
task = @async while true
154+
events = try wait(watcher)
155+
catch err
156+
err isa EOFError && break
157+
rethrow()
180158
end
181-
end
182-
events = 0
183-
action != CURL_POLL_IN && (events |= UV_WRITABLE)
184-
action != CURL_POLL_OUT && (events |= UV_READABLE)
185-
event_cb = @cfunction(event_callback, Cvoid, (Ptr{Cvoid}, Cint, Cint))
186-
uv_poll_start(uv_poll_p, events, event_cb)
187-
elseif action == CURL_POLL_REMOVE
188-
if uv_poll_p != C_NULL
189-
uv_poll_stop(uv_poll_p)
190-
uv_close(uv_poll_p, cglobal(:jl_free))
159+
flags = CURL_CSELECT_IN * isreadable(events) +
160+
CURL_CSELECT_OUT * iswritable(events) +
161+
CURL_CSELECT_ERR * events.disconnect
191162
lock(multi.lock) do
192-
@check curl_multi_assign(multi.handle, sock, C_NULL)
163+
@check curl_multi_socket_action(multi.handle, sock, flags)
164+
check_multi_info(multi)
193165
end
194166
end
195-
else
196-
@async @error("socket_callback: unexpected action", action)
197-
return -1
167+
@isdefined(errormonitor) && errormonitor(task)
198168
end
169+
@isdefined(old_watcher) && close(old_watcher)
199170
return 0
200171
end
201172

202173
function add_callbacks(multi::Multi)
203-
# stash multi handle pointer in timer
204174
multi_p = pointer_from_objref(multi)
205-
## TODO: use a member access API
206-
unsafe_store!(convert(Ptr{Ptr{Cvoid}}, multi.timer), multi_p)
207175

208176
# set timer callback
209177
timer_cb = @cfunction(timer_callback, Cint, (Ptr{Cvoid}, Clong, Ptr{Cvoid}))

src/Curl/utils.jl

+5-67
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
# basic C stuff
2-
31
if !@isdefined(contains)
42
contains(haystack, needle) = occursin(needle, haystack)
53
export contains
64
end
75

6+
# basic C stuff
7+
88
puts(s::Union{String,SubString{String}}) = ccall(:puts, Cint, (Ptr{Cchar},), s)
99

1010
jl_malloc(n::Integer) = ccall(:jl_malloc, Ptr{Cvoid}, (Csize_t,), n)
1111

12-
# check if a function or C call failed
12+
# check if a call failed
1313

14-
function check(ex::Expr, lock::Bool)
14+
macro check(ex::Expr)
1515
ex.head == :call ||
1616
error("@check: not a call: $ex")
1717
arg1 = ex.args[1] :: Symbol
@@ -24,75 +24,13 @@ function check(ex::Expr, lock::Bool)
2424
f = arg1
2525
end
2626
prefix = "$f: "
27-
ex = esc(ex)
28-
if lock
29-
ex = quote
30-
Base.iolock_begin()
31-
value = $ex
32-
Base.iolock_end()
33-
value
34-
end
35-
end
3627
quote
37-
r = $ex
28+
r = $(esc(ex))
3829
iszero(r) || @async @error($prefix * string(r))
3930
r
4031
end
4132
end
4233

43-
macro check(ex::Expr) check(ex, false) end
44-
macro check_iolock(ex::Expr) check(ex, true) end
45-
46-
# some libuv wrappers
47-
48-
const UV_READABLE = 1
49-
const UV_WRITABLE = 2
50-
51-
function uv_poll_alloc()
52-
# allocate memory for: uv_poll_t struct + extra for curl_socket_t
53-
jl_malloc(Base._sizeof_uv_poll + sizeof(curl_socket_t))
54-
end
55-
56-
function uv_poll_init(p::Ptr{Cvoid}, sock::curl_socket_t)
57-
@check_iolock ccall(:uv_poll_init, Cint,
58-
(Ptr{Cvoid}, Ptr{Cvoid}, curl_socket_t), Base.eventloop(), p, sock)
59-
end
60-
61-
function uv_poll_start(p::Ptr{Cvoid}, events::Integer, cb::Ptr{Cvoid})
62-
@check_iolock ccall(:uv_poll_start, Cint,
63-
(Ptr{Cvoid}, Cint, Ptr{Cvoid}), p, events, cb)
64-
end
65-
66-
function uv_poll_stop(p::Ptr{Cvoid})
67-
@check_iolock ccall(:uv_poll_stop, Cint, (Ptr{Cvoid},), p)
68-
end
69-
70-
function uv_close(p::Ptr{Cvoid}, cb::Ptr{Cvoid})
71-
Base.iolock_begin()
72-
ccall(:uv_close, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), p, cb)
73-
Base.iolock_end()
74-
end
75-
76-
function uv_timer_init(p::Ptr{Cvoid})
77-
@check_iolock ccall(:uv_timer_init, Cint,
78-
(Ptr{Cvoid}, Ptr{Cvoid}), Base.eventloop(), p)
79-
end
80-
81-
function uv_timer_start(p::Ptr{Cvoid}, cb::Ptr{Cvoid}, t::Integer, r::Integer)
82-
@check_iolock ccall(:uv_timer_start, Cint,
83-
(Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64), p, cb, t, r)
84-
end
85-
86-
function uv_timer_stop(p::Ptr{Cvoid})
87-
@check_iolock ccall(:uv_timer_stop, Cint, (Ptr{Cvoid},), p)
88-
end
89-
90-
# additional libcurl methods
91-
92-
function curl_multi_socket_action(multi_handle, s, ev_bitmask)
93-
LibCURL.curl_multi_socket_action(multi_handle, s, ev_bitmask, Ref{Cint}())
94-
end
95-
9634
# curl string list structure
9735

9836
struct curl_slist_t

0 commit comments

Comments
 (0)