Atul Bhosale

20 Feb 2018

Using Ruby 2.5's IO#close

Ruby 2.5 was recently released.

IO class is the basis for all input and output. You can use the public methods of IO class to open, write, read and close the IO stream. The close method makes the IO stream unavailable for any further data operations.  Ruby’s IO#close which would earlier raise an error with message “stream closed”, but it is refined to “stream closed in another thread”. The new message is more clear for the user.

  read_io, write_io = IO.pipe
  thread = Thread.new do
    read_io.read
  end
  read_io.close
  write_io.close
  thread.join

Ruby 2.4.0

  ➜  ~ ruby io_ruby.rb
  io_ruby.rb:3:in `read': closed stream (IOError)
          from io_ruby.rb:3:in `block in <main>'

Ruby 2.5.0

  ➜  ~ ruby io_ruby.rb
  #<Thread:0x00007f9d918a4900@io_ruby.rb:2 run> terminated with exception (report_on_exception is true):
  Traceback (most recent call last):
          1: from io_ruby.rb:3:in `block in <main>'
  io_ruby.rb:3:in `read': closed stream (IOError)
  Traceback (most recent call last):
          1: from io_ruby.rb:3:in `block in <main>'
  io_ruby.rb:3:in `read': closed stream (IOError)

In the above example we close the read stream before the thread has finished its execution and an IOError is raised with message “closed stream” for both Ruby 2.4.0 & Ruby 2.5.0.

Didn’t we try to close the stream in other thread besides the main ruby thread? No, I don’t think we did. Can we tell the thread scheduler that “hey, please pass the execution to another thread other than the one which is currently running”? Yes, we can using 

  Thread.pass

Lets update our program

  read_io, write_io = IO.pipe
  thread = Thread.new do
    read_io.read
  end
  Thread.pass
  read_io.close
  write_io.close
  thread.join

Ruby 2.4.0

  io_ruby.rb:3:in `read': closed stream (IOError)
          from io_ruby.rb:3:in `block in <main>'

Ruby 2.5.0

  #<Thread:0x00007f96e60a53a0@io_ruby.rb:2 run> terminated with exception (report_on_exception is true):
  Traceback (most recent call last):
          1: from io_ruby.rb:3:in `block in <main>'
  io_ruby.rb:3:in `read': closed stream (IOError)
  Traceback (most recent call last):
          1: from io_ruby.rb:3:in `block in <main>'
  io_ruby.rb:3:in `read': closed stream (IOError)

Why aren’t we getting “stream closed in another thread” error message? Is our thread sleeping or dead? We should also check if the our thread is dead or sleeping.

  #<Thread:0x00007f96e60a53a0@io_ruby.rb:2 run> terminated with exception (report_on_exception is true):
  read_io, write_io = IO.pipe
  thread = Thread.new do
    read_io.read
  end
  Thread.pass until thread.stop?
  read_io.close
  write_io.close
  thread.join

Ruby 2.4.0

  io_ruby.rb:3:in `read': stream closed (IOError)
          from io_ruby.rb:3:in `block in <main>'

Ruby 2.5.0

  #<Thread:0x00007f9cfc889370@io_ruby.rb:2 run> terminated with exception (report_on_exception is true):
  Traceback (most recent call last):
          1: from io_ruby.rb:3:in `block in <main>'
  io_ruby.rb:3:in `read': stream closed in another thread (IOError)
  Traceback (most recent call last):
          1: from io_ruby.rb:3:in `block in <main>'
  io_ruby.rb:3:in `read': stream closed in another thread (IOError)

There you go. We got our error message “stream closed in another thread”

Tags

comments powered by Disqus