How to stop a thread blocked by readline?

Hey all :slight_smile:

I want to use a thread redirecting stderr. The thread should stop after a Threads.Atomic is set to false.

My current example is blocked on readline and stops only after something is written to it. Is there a way to stop the thread immediately?

Here is my little example:

julia> (w, r) = redirect_stderr()
(Base.PipeEndpoint(Base.Libc.WindowsRawSocket(0x000000000000bdf0) open, 0 bytes waiting), Base.PipeEndpoint(Base.Libc.WindowsRawSocket(0x000000000000bd14) open, 0 bytes waiting))

julia> IS_RUNNING = Threads.Atomic{Bool}(true)
Base.Threads.Atomic{Bool}(true)

julia> t = @async while IS_RUNNING[]
           line = readline(w)
           println(line)
       end
Task (runnable) @0x00000000437d73d0

julia> println(stderr, "Error")
Error

julia> IS_RUNNING[] = false;

julia> t |> istaskdone
false

Thanks

The only way I know how would be to close(w). When the IO is closed readline will throw an exception I believe.

I propose the following function:

function switch(s)
    if s == :on
        doit[1] = true
        rd = redirect_stderr()[1]
        @async while doit[1]
            println("Test: ", readline(rd))
        end
    elseif s == :off
        doit[1] = false
        println(stderr, s)  # solution: give it something to read !!
        redirect_stderr(old_stderr)
    else
        println(stderr, "not allowed: $s")
    end
end

then you can do (needs still 2 global variables):

julia> old_stderr = stderr
Base.TTY(RawFD(22) open, 0 bytes waiting)

julia> doit = [false];

julia> t = switch(:on)
Task (runnable) @0x000000016704f860

julia> println(stderr, "test 1")
Test: test 1

julia> println(stderr, "test 2")
Test: test 2

julia> switch(:off)
Test: off
Base.TTY(RawFD(22) open, 0 bytes waiting)

julia> println(stderr, "test 3")
test 3

julia> t
Task (done) @0x000000016704f860

This can be simplified to:

function switch2(s)
    my_switch = "sloBMBS90eXNG"
    if s == :on
        rd = redirect_stderr()[1]
        @async while true
            line = readline(rd)
            line != my_switch ? println("Test2: ", line) : break
        end
    elseif s == :off
        println(stderr, my_switch)
        redirect_stderr(old_stderr)
    else
        println(stderr, "not allowed: $s")
    end
end

julia> t = switch2(:on)
Task (runnable) @0x00000001671baba0

julia> println(stderr, "test 1")
Test2: test 1

julia> switch2(:off)
Base.TTY(RawFD(22) open, 0 bytes waiting)

julia> println(stderr, "test 2")
test 2

julia> t
Task (done) @0x00000001671baba0

only the old_stderr is needed as a global variable to restore stderr.

the following doesnโ€™t need even global old_stderr. We capture stderr when we switch on and let the task itself restore it.

function switch3(s)
    my_switch = "sloBMBS90eXNG"
    if s == :on
        if stderr isa Base.TTY
            old_stderr = stderr
            rd = redirect_stderr()[1]
            @async while true
                line = readline(rd)
                line != my_switch ? 
                    println("Test3: ", line) : 
                    (redirect_stderr(old_stderr); break)
            end
        end
    elseif s == :off
        println(stderr, my_switch)
    else
        println(stderr, "not allowed: $s")
    end
end

julia> t = switch3(:on)
Task (runnable) @0x000000011331f640

julia> println(stderr, "test 1")
Test3: test 1

julia> switch3(:off)

julia> println(stderr, "test 2")
test 2

julia> t
Task (done) @0x000000011331f640

That should be fine now. What do you think?