Another Programming Ruby Question

This is from Chapter 11 titled “Threads and Processes” of Programming
Ruby book on page 139.

I am pasting the source code below:

Sample code from Programing Ruby, page 131

$stderr.sync = $stdout.sync = true
Thread.abort_on_exception = true
threads = []
4.times do |number|
threads << Thread.new(number) do |i|
raise “Boom!” if i == 2
print “#{i}\n”
end
end
threads.each {|t| t.join }

Note that the page numbers in the downloaded code with the actual page
numbers do not match - probably due to two different Editions of the
book. Has not been a problem thus far.

When I run the code in SCITE, I get the following output:

ruby ex0318.rb
ex0318.rb:7: Boom! (RuntimeError)
from ex0318.rb:6:in initialize' from ex0318.rb:6:innew’
from ex0318.rb:6
from ex0318.rb:5:in `times’
from ex0318.rb:5
Exit code: 1

Now the output shown in the book is different. It shows that a 0
followed by 1 are going to be output (on separate lines) before the
program hits the exception condition. I commented out the line which
says

$stderr.sync = $stdout.sync = true as follows:

Sample code from Programing Ruby, page 131

$stderr.sync = $stdout.sync = true

Thread.abort_on_exception = true
threads = []
4.times do |number|
threads << Thread.new(number) do |i|
raise “Boom!” if i == 2
print “#{i}\n”
end
end
threads.each {|t| t.join }

and re-ran the program.

This time I got the right output as below:

ruby ex0318.rb
0
1
ex0318.rb:7: Boom! (RuntimeError)
from ex0318.rb:6:in initialize' from ex0318.rb:6:innew’
from ex0318.rb:6
from ex0318.rb:5:in `times’
from ex0318.rb:5
Exit code: 1

That set me thinking what does the line:
$stderr.sync = $stdout.sync = true
actually do?
I looked it up into the book and googled for it, but cannot find an
answer that makes sense. Would someone kindly explain in Ruby/Newby
terms? When to use it when not to use it etc? I know that this is
setting both standard error and standard output to the same channel.
What does the sync method do? What does it mean to set it to true? If
there are online resources that can be looked up for these kinds of
questions I will not bother you. But the online Ruby API almost mirrors
Dave T.'s Programming Ruby book which I am working with anyway.
Thanks in advance for your time.
Bharat

Bharat R. [email protected] writes:

That set me thinking what does the line:
$stderr.sync = $stdout.sync = true
actually do?

(from the “sync= (IO)” section of RDoc Documentation):

ios.sync = boolean => boolean

Sets the ``sync mode’’ to true or false. When sync mode is true, all
output is immediately flushed to the underlying operating system and
is not buffered internally. Returns the new state. See also IO#fsync.

f = File.new(“testfile”)
f.sync = true

(produces no output)

When data is output through an IO handle, it might be buffered. What
this means is that the data is held until a certain number of bytes are
received or until certain characters are fed to the stream (such as
newline), and only then are they sent to the output destination.

The “sync mode” determines whether this buffering takes place … if
it’s set to true, then there is no such buffering, and the data is
“synchronized” with the output destination immediately upon being sent
through the IO handle.

The statement you refer to above makes sure that $stdout and $stderr are
both synchronized. Therefore, in the thread, data written to these
handles will be output immediately. Without your statement, there could
be a delay between the moment that the data is sent to $stdout or
$stderr and it shows up on the terminal. That’s why you are seeing the
output in a different sequence, depending on whether or not the sync
mode is set.

As you can see, this has nothing to do with setting $stdout and $stderr
to be in sync with each other.

Hello Lloyd,
I appreciate your time and detailed response.
It seems to me that I should see the opposite behavior to what I am
seeing based on the explanation that you provided. That is:
If setting sync to true causes no buffering then I should actually
observe the first two values (0 and 1) to be immediately output.
Instead, I see the exception messages as I show above. Since the
exception does not get raised until i reaches a value of 2, I should see
the 0 and 1 printed to the standard output and then I should see
exception being printed to standard error?
Bharat

Thank you Mauricio. This makes perfect sense now.

On a separate note to all Ruby Newby persons: I am finding that
Programming Ruby or the PickAxe book almost should be your 2nd or 3rd
Ruby book. I am glad that I bought David Black’s Ruby for Rails book
and went through it entirely before coming back to Programming Ruby
book. I have also bought Everyday Scripting based on my bookstore
browsing and am planning on going through it next. I will continue to
work through Dave T.'s Programming Ruby book though. My advice to
everyone based on my experience is that don’t get frustrated and ask
questions on this forum for clarifications. There are some very kind,
patient, and knowledgable people who are willing to spare their time and
facilitate the learning process.

Regards,
Bharat

On Thu, Feb 15, 2007 at 05:40:30AM +0900, Bharat R. wrote:

threads.each {|t| t.join }
[…]
When I run the code in SCITE, I get the following output:

ruby ex0318.rb
ex0318.rb:7: Boom! (RuntimeError)
from ex0318.rb:6:in initialize' from ex0318.rb:6:innew’
from ex0318.rb:6
from ex0318.rb:5:in `times’
from ex0318.rb:5
Exit code: 1

The example assumes that the threads will be executed serially, and
those with
i=0,1 will terminate before the third one raises a RuntimeError.

However, when you set $stdout.sync = true each time you printf
whatever
Ruby will try to schedule another thread. In the above example, the two
first
threads run until print “#{i}\n” and the third thread, which raises an
exception, is scheduled before the numbers are actually written to
$stdout.

If you change the example so that the threads are rescheduled before the
next
one is created, you’ll get the expected output:

$ ruby
$stderr.sync = $stdout.sync = true
Thread.abort_on_exception = true
threads = []
4.times do |number|
threads << Thread.new(number) do |i|
raise “Boom!” if i == 2
print “#{i}\n”
end
sleep 0.01
end
threads.each {|t| t.join }
^D
0
1
-:6: Boom! (RuntimeError)
from -:5:in initialize' from -:5:innew’
from -:5
from -:4:in `times’
from -:4

IO#sync does work the way it should, but the example is confusing.