JuliaWeb / HTTP.jl

HTTP for Julia
https://juliaweb.github.io/HTTP.jl/stable/
Other
626 stars 177 forks source link

WebSockets error trace when using PrecompileTools #1180

Open attdona opened 1 month ago

attdona commented 1 month ago

Versions:

I'm using HTTP and PrecompileTools for my package and I get:

[pid 860579] waiting for IO to finish:
 Handle type        uv_handle_t->data
 timer              0x297fc50->0x7ab870880e20
This means that a package has started a background task or event source that has not finished running. For precompilation to complete successfully, the event source needs to be closed explicitly. See the developer documentation on fixing precompilation hangs for more help.

The culprit is a timer defined here: https://github.com/JuliaWeb/HTTP.jl/blob/master/src/WebSockets.jl#L598

I think something like the following should work. To get code simple, the if around the Timer expression got removed, because probably it is redundant, but I'm not 100% sure about that.

function Base.close(ws::WebSocket, body::CloseFrameBody=CloseFrameBody(1000, ""))
    isclosed(ws) && return
    @debugv 2 "$(ws.id): Closing websocket"
    ws.writeclosed = true
    data = Vector{UInt8}(body.message)
    prepend!(data, reinterpret(UInt8, [hton(UInt16(body.status))]))
    try
        writeframe(ws.io, Frame(true, CLOSE, ws.client, data))
    catch
        # ignore thrown errors here because we're closing anyway
    end
    # if we're initiating the close, wait until we receive the
    # responding close frame or timeout
    timer = Timer(5) do t
        ws.readclosed = true
        !ws.client && isopen(ws.io) && close(ws.io)
    end
    while !ws.readclosed
        try
            receive(ws)
        catch
            # ignore thrown errors here because we're closing anyway
            # but set readclosed so we don't keep trying to read
            ws.readclosed = true
        end
    end
    # we either recieved the responding CLOSE frame and readclosed was set
    # or there was an error/timeout reading it; in any case, readclosed should be closed now
    @assert ws.readclosed

    # close the timer
    close(timer)

    # if we're the server, it's our job to close the underlying socket
    !ws.client && isopen(ws.io) && close(ws.io)
    return
end