Skip to content

send() / recv() on UDPSocket is not async-safe #57001

@ettersi

Description

@ettersi

The following code tries to send a UDP packet to socat -v PIPE udp-recvfrom:9809,fork and then receive the echo response, but it consistently fails.

julia> using Sockets

julia> sock = UDPSocket()
       bind(sock, ip"127.0.0.1", 0)

       errormonitor(@async println(String(recv(sock))))
       send(sock, ip"127.0.0.1", 9809, "hello")

       sleep(1)
       close(sock)

Unhandled Task ERROR: EOFError: read end of file
Stacktrace:
 [1] try_yieldto(undo::typeof(Base.ensure_rescheduled))
   @ Base ./task.jl:958
 [2] wait()
   @ Base ./task.jl:1022
 [3] wait(c::Base.GenericCondition{Base.Threads.SpinLock}; first::Bool)
   @ Base ./condition.jl:130
 [4] wait
   @ ./condition.jl:125 [inlined]
 [5] recvfrom(sock::UDPSocket)
   @ Sockets ~/.julia/juliaup/julia-1.11.2+0.x64.apple.darwin14/share/julia/stdlib/v1.11/Sockets/src/Sockets.jl:359
 [6] recv
   @ ~/.julia/juliaup/julia-1.11.2+0.x64.apple.darwin14/share/julia/stdlib/v1.11/Sockets/src/Sockets.jl:324 [inlined]
 [7] (::var"#7#8")()
   @ Main ./REPL[6]:4

The problem is that the send() "activates" the UDP socket and yields to the async task, and then the recv() notices that the socket is already "active" and skips registering the libuv callback.

if ccall(:uv_is_active, Cint, (Ptr{Cvoid},), sock.handle) == 0
err = ccall(:uv_udp_recv_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}),
sock,
@cfunction(Base.uv_alloc_buf, Cvoid, (Ptr{Cvoid}, Csize_t, Ptr{Cvoid})),
@cfunction(uv_recvcb, Cvoid, (Ptr{Cvoid}, Cssize_t, Ptr{Cvoid}, Ptr{Cvoid}, Cuint)))

Original discussion: https://discourse.julialang.org/t/async-udp-socket-usage/

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions