Switching tasks mid-write()?

I am running into a problem with my logging implementation, and I boiled it down to the following:

function repro()
       @async write(stderr, "Foo", "\n")
       @async write(stderr, "Bar", "\n")
       nothing
end
repro()

100% of the time I get:

FooBar

I appreciate that write() can yield, but should it at least complete the write before doing so? Otherwise I have to manually concatenate everything if I want to make sure things stay on the same line.

You can use print instead, which works like you expect (it locks the io and prints everything). Otherwise you can write everything to a IOBuffer and then write everything in one go:

function repro()
    @async begin
        io = IOBuffer()
        write(io, "Foo", "\n")
        write(stderr, take!(io))
    end
    @async begin
        io = IOBuffer()
        write(io, "Bar", "\n")
        write(stderr, take!(io))
    end
    nothing
end
repro()

This is the method that the logging system is using, for example.

One other thing I noticed is if I used IOBuffer() instead of stderr I get entirely different thing:

function repro()
       io = IOBuffer()
       t1 = @async write(io, "Foo", "\n")
       t2 = @async write(io, "Bar", "\n")
       wait(t1)
       wait(t2)
       String(take!(io))
end

repro()

I get “Foo\nBar\n” as expected. Same for writing to a file – no interleaving happens there. There must be something special about writing to stderr/stdout, and that seems strange.

Thank you for pointing out print() is different. I didn’t realize that, and I could borrow the trick.

I am not entirely sure which is better from performance perspective – allocating a buffer + memcpy() or locking stderr (which will cause some unnecessary task switching, as write() I assume will still yield)

Thanks a lot!

There are only certain points where tasks “yield” to each other. IO is one of them.

yes, but it yields when writing to stderr, but not to a filestream.