lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great Jose Torre-Bueno once stated:
> Can anyone explain why this code produces a line on the console that ticks
> up every second:

  You are running this on a Unix system, right?

> function testw()
> 	while true do
> 		t = os.time()
> 		io.write(t, '\r')
> 	end
> end
> 
> As does this (with less cursor movement)
> 
> function testw()
> 	while true do
> 		t = os.time()
> 		io.write(t, '\r')
> 		while os.time() == t do
> 			io.write('\r')  
> 		end
> 	end
> end
> 
> But this code produces nothing:
> 
> function testw()
> 	while true do
> 		t = os.time()
> 		io.write(t, '\r')
> 		while os.time() == t do
> 			io.write('')  
> 		end
> 	end
> end

  Using the C standard IO (which is what Lua uses), you have three modes of
buffering, none, line and full.  No buffering causes the output to be
written immediately.  Line buffering waits until an entire line is present
before output (or the internal buffer is full).  Full buffering waits until
the internal buffer is full before writing it.  

  Now, on Unix, output to a TTY (like the command line) defaults to line
buffering; everything else defaults to full buffering.  Also, the default
buffer size is BUFSIZ (an implementation dependent value) but is typically
around 8,192 bytes in size.  

  '\r' on Unix causes the TTY cursor to move to the start of the current
line (it's CARRIAGE RETURN) whereas '\n' causes the cursror to move to the
start of the next line (LINE FEED) [1].  Your first example will call
os.time() and print the output thousands of time per second, and thus, the
internal buffer is filled up and thus, flushed to the actual output device. 
That's why you are seeing the output.  

  The second example calls os.time() once per second, but writes thousands
of '\r' per second, again, filling up the internal buffer and thus causing
it to be flushed.

  Your third example is calling os.time() once per second, writing it once
per second, then writing nothing thousands of times per second (thus, not
fulling up the internal buffer).  Assuming a standard 8,192 byte internal
buffer, and given that os.time() returns a 10-digit number, it would take
819 seconds (a little over 13 and a half minutes) for the output to be
flushed.  

> As does this:
> 
> function testw()
> 	while true do
> 		t = os.time()
> 		io.write(t, '\r')
> 		while os.time() == t do
> 		end
> 	end
> end
> 
> 
> It almost seems like an os call creates a need to flush the stdout buffer.

  Nope.  It's just that C standard library is buffering the output.  

> BTW replacing \r with \n in the lines that writes the time creates a
> scrolling display as expected in all cases, it seems to be some
> interaction with \r in particular.
> 
> On the other hand this:
> 
> function testw()
> 	while true do
> 		t = os.time()
> 		io.write(t, '\r')
> 		os.execute('sleep 1')
> 		io.write('\r')
> 	end
> end

  Try adding io.stdout:setvbuf('no') and see what happens.  This turns off
buffering.

  -spc

[1]	The speficiation for CR (CARRIAGE RETRN) and LF (LINE FEED) is a bit
	unclear on what exactly should happen.  There are two separate
	actions that can happen:

		The carriage head [2] moves back to the left edge.
		The output is advanced one line.

	Microsoft used a very literal definition for this, and thus, the two
	characters are required to start output on the next line (CR LF). 
	Unix defines one character (LF) to start output on the next
	line---it's up the device driver on Unix to do The Right Thing when
	it sees a LF.  I have also used other systems that used CR to start
	output on the next line.

	In C on Windows, the character '\n' specifies the action "start
	output on the next line", and it's the C standard IO library that
	sends the CR and LF [3].

	Now, on both Unix and Windows, the CR character (defined in C as
	'\r') just sends the cursor to the begining of the current line.

[2]	These were defined back in an age of hard output.

[3]	Except if the FILE* is opened in binary mode, in which case, it does
	NOT send the CRLF when it sees a '\n' but instead only sends LF
	(which is the Unix standard).