Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stdout doesn't flush correctly #238

Closed
randy3k opened this issue Oct 16, 2014 · 20 comments · Fixed by #240 or #392
Closed

stdout doesn't flush correctly #238

randy3k opened this issue Oct 16, 2014 · 20 comments · Fixed by #240 or #392

Comments

@randy3k
Copy link
Contributor

randy3k commented Oct 16, 2014

screen shot 2014-10-16 at 1 48 51 am

ccall((:printf, "libc"), Int, (Ptr{Uint8},), "abc");

It happens when I select "run all". It works fine if I only run that cell. It may be also related to #172.

Edit:

It also seems that flushing doesn't help.

screen shot 2014-10-16 at 1 49 40 am

@stevengj
Copy link
Member

What operating system do you have? This may be a libc problem.

@randy3k
Copy link
Contributor Author

randy3k commented Oct 16, 2014

I am using Mac OS X 10.10, julia 0.3.1.

@randy3k
Copy link
Contributor Author

randy3k commented Oct 16, 2014

interesting enough, if i set IJulia.set_verbose(), it works expertedly.

@stevengj
Copy link
Member

I think that this is an IPython issue. The basic problem here is that stdio output is sent to IPython via stream messages which aren't attached to any particular output cell. So, when you execute many cells in quick succession, there is a race condition that occurs: depending on when the stream message arrives at IPython, that determines where it is displayed.

Turning on verbose mode probably slows things down just enough that the stream message arrives before IPython moves on to the next code cell.

cc: @minrk

@minrk
Copy link
Contributor

minrk commented Oct 16, 2014

There shouldn't be any race condition on the frontend. The kernel decides where output goes by setting the parent_header of output messages. Are there race conditions or async events happening in the kernel itself? Can the idle message that indicates a cell is complete be sent before an output message?

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

so, is it possible to attach stream message to a output cell?

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

After some preliminary investigation, I found that the reader never works. takebuf_string(read_stdout.buffer) is always empty no matter what.
The flushing of stdout is done by the watcher. Since the watcher is running async. It also explains why the output is out of async.

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

Sorry for the multiple references...I kept doing stupid things...you should check this one https://github.com/randy3k/IJulia.jl/commit/9104817dde3f7cb24e66d10d3ee15c8c74dd351f

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

It seems that the fix only relief the problem but not solve the problem. If display invokes a c call to printf, we should also flush the stdout.

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

Check https://github.com/JuliaLang/IJulia.jl/pull/239/files I believe I solved it.

@vtjnash
Copy link
Member

vtjnash commented Oct 17, 2014

This is tough, since Julia uses async io everywhere (like JavaScript/nodejs) unlike more traditional languages, which sometimes are more synchronous. flush is an extremely weak guarantee ("make sure my libc file buffers are emptied"), but for something small and local also happens to be enough, since the kernel likes to transfer small amounts of data eagerly. Readavailable is the equivalent of flush on the receive side ("give me everything that was in my libc buffer already"). Both of these fail to acknowledge that io needs actual external structure (such as a \0 inserted into the stream), not timing-based assumptions.

stevengj added a commit that referenced this issue Oct 17, 2014
…r const flag, make some changes for #238 (not fixed yet)
@stevengj
Copy link
Member

(Note that, if I recall correctly, IPython does not even attempt to capture stdout from external C code, only Python stdout, so we are being a bit more ambitious here and life is a bit harder here because we are dependent on libc.)

@vtjnash
Copy link
Member

vtjnash commented Oct 17, 2014

What does IPython do with data printed from a thread? That is quite similar to life in Julia

@minrk
Copy link
Contributor

minrk commented Oct 17, 2014

What does IPython do with data printed from a thread?

Routing is handled entirely by the msg_id in the parent_header. Each msg_id is associated with an output area. Since IPython stores the parent in a single location that is looked up when output messages are constructed, background thread IO will be associated with the most recently executed cell.

@stevengj
Copy link
Member

IJulia also stores the parent in a single location (the global execute_msg) that is looked up when output messages are constructed, so the behavior is similar.

The issue here is just to make sure that buffers are flushed at the end of an execute request, so that we capture all available I/O to send at that time. If the I/O system decides not to make the bytes available to read until later (e.g. because of some hidden buffering?) then the output will get attached to a later execution cell... IJulia has no way of knowing where stdio should go except via the time that the bytes are available.

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

it seems that flush_cstdio() is not synchronized, we have to add a little delay. https://github.com/randy3k/IJulia.jl/commit/a7a2d96b98fcc38c325f3820f3041640e528b19c

screen shot 2014-10-17 at 12 18 42 pm

edit: also, we have to flush again after pyout for something like the first cell.

@stevengj
Copy link
Member

Adding some arbitrary delay seems quite fragile to me. I would rather call some (platform-specific?) "really flush this stream" library routine, but that might not always be possible. (e.g. consider the case of an external library that uses a custom buffering I/O library ... for example, some Fortran implementations might do this).

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

i agree, it is just to demonstrate what was causing the problem.

@randy3k
Copy link
Contributor Author

randy3k commented Oct 17, 2014

thing gets more complicated if we want to align the "foo" and "a" in the above example, since ipython doesn't know the order of execution..

@stevengj
Copy link
Member

Julia doesn't know the order of execution either: it doesn't know when or where anything in the stdout buffer came from, especially output originating in external libraries. The basic issue is that there are multiple I/O buffers involved here, some of which are not under Julia's control.

JobJob added a commit to JobJob/IJulia.jl that referenced this issue Nov 27, 2015
Instead of sleeping during the read loop to throttle the number of
stream messages sent, we now continually read the read_stdout and
read_stderr streams (whose buffers are limited to 64KB on OSX/Linux, 4KB
Windows?) and add data read into our own IOBuffers for STDOUT/STDERR.

The rate of sending is now controlled by a stream_interval parameter; a
stream message is now sent at most once every stream_interval seconds
(currently 0.1). The exception to this is if the buffer has reached the
size specified in max_size (currently 10KB), and will then be sent
immediately. This is to avoid overly large stream messages being sent.

Improves flush() so that it will have a very high chance of flushing data
already written to stdout/stderr.

The above changes fix JuliaLang#372 JuliaLang#342 JuliaLang#238 347

Adds timestamps to the debug logging and the task which the vprintln
call is made from.

Fixes using ?keyword (e.g. ?try)
JobJob added a commit to JobJob/IJulia.jl that referenced this issue Nov 27, 2015
Instead of sleeping during the read loop to throttle the number of
stream messages sent, we now continually read the read_stdout and
read_stderr streams (whose buffers are limited to 64KB on OSX/Linux, 4KB
Windows?) and add data read into our own IOBuffers for STDOUT/STDERR.

The rate of sending is now controlled by a stream_interval parameter; a
stream message is now sent at most once every stream_interval seconds
(currently 0.1). The exception to this is if the buffer has reached the
size specified in max_size (currently 10KB), and will then be sent
immediately. This is to avoid overly large stream messages being sent.

Improves flush() so that it will have a very high chance of flushing data
already written to stdout/stderr.

The above changes fix JuliaLang#372 JuliaLang#342 JuliaLang#238 JuliaLang#347

Adds timestamps to the debug logging and the task which the vprintln
call is made from.

Fixes using ?keyword (e.g. ?try)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants