Inducing a Julia Task to finish

Viewed 208

I have an application where I have a middleman function that is listening on a Julia Channel for data and immediately writing the data to an HTTP.WebSocket object. There is some sort of client at the other end of the websocket, let's say it's JavaScript running in a browser. I have created a Task object that is bound to the middleman function. I would like the client to, say, close the websocket, and have that action cause the Task to finish. Here's what my middleman looks like:

function listener(port::Int64, channel::Channel)
    HTTP.listen(Sockets.localhost, port) do http::HTTP.Stream
        if HTTP.WebSockets.is_upgrade(http.message)
            HTTP.WebSockets.upgrade(http, binary=true) do ws
                try
                    while !eof(ws)
                        _ = readavailable(ws)
                        bytes = take!(channel)
                        write(ws, bytes)
                    end
                catch err
                    if isa(err, InvalidStateException)
                        @info "Channel for $port is closed, exiting"
                    end
                end
            end
        end
    end
end

What I see is that listener() reacts to the websocket's being closed - it catches an InvalidStateException - but the Task's state remains :runnable. You'll notice that I'm currently ignoring the content of the message sent by the client (_ = readavailable(ws)). I've done some experimenting with checking for a special value (e.g. "done"), but haven't had any better luck with that approach than just having the client close the websocket.

1 Answers

The only way I've found to kill a task from outside a networking task is to send an interrupt exception. For example, I wanted to use Revise with Mux and Mux wouldn't remap any functions or lambdas properly unless I restarted Mux. I didn't want to close julia, I just wanted to restart the Mux.serve task.

global MUX_SERVER_TASK = nothing
function revise_mux_routes(port=8080)
    # println("Revise works!")
    global MUX_SERVER_TASK
    if !isnothing(MUX_SERVER_TASK)
        @async Base.throwto(MUX_SERVER_TASK, InterruptException())
        while !(istaskdone(MUX_SERVER_TASK) || istaskfailed(MUX_SERVER_TASK))
            yield()
        end
        println("killed MUX_SERVER_TASK")
        @show MUX_SERVER_TASK
    end

    MUX_SERVER_TASK = Mux.serve(APP, port; stream=true, readtimeout=0)
    @show MUX_SERVER_TASK
end

This same sort of function should work with killing your listen task.

Related