Exception never raised when threading an eval containing a malformed command [might be a bug ?]

Hello everyone!

I've encountered a strange issue when coding a tool extending ruby, and was wondering if I was missing something... I'm working under windows with the last stable release of ruby (1.8.5-21) and did not find a way to debug ruby sources to observe the behaviour. This is reproductible with just the common ruby interpreter. Let me explain the problem :

First let's consider this small script :

  #!/usr/bin/env ruby
  $FOO+2

We get the evident result ($FOO is not defined, an exception's raised) :

  test.rb:2: undefined method `+' for nil:NilClass (NoMethodError)

Then let's consider this small script :

  #!/usr/bin/env ruby
  eval("puts \"foo")

You can see that the string given to puts is malformed, so we get again an exception :

  (eval):1: compile error (SyntaxError)
  (eval):1: unterminated string meets end of file

Ok. Now let's thread this. Let's consider the following script :

  #!/usr/bin/env ruby
  def newthread()
    toret = Thread.new {
    puts "START"
    begin
      $FOO+2
    rescue
      puts("EXCEPTION !!!!!!!!")
    end
    puts "END"
    }
    return toret
  end

  $TESTTHREAD = newthread()
  2.times {
    if(!$TESTTHREAD.alive?) then
      puts "DEAD, RESURRECTING"
      $TESTTHREAD = newthread()
      sleep(4)
    end
  }

We create a thread, provoke an exception, catch it, restart the thread, and so on twice again. We get the following expected behaviour :

  START
  EXCEPTION !!!!!!!!
  END
  DEAD, RESURRECTING
  START
  EXCEPTION !!!!!!!!
  END
  DEAD, RESURRECTING
  START
  EXCEPTION !!!!!!!!
  END

But now, let's consider this (here comes the issue) : we do exactly the same thing (we only replace the $FOO+2 line by our malformed eval-puts) :

  #!/usr/bin/env ruby
  def newthread()
    toret = Thread.new {
    puts "START"
    begin
      eval("puts \"foo")
    rescue
      puts("EXCEPTION !!!!!!!!")
    end
    puts "END"
    }
    return toret
  end

  $TESTTHREAD = newthread()
  2.times {
    if(!$TESTTHREAD.alive?) then
      puts "DEAD, RESURRECTING"
      $TESTTHREAD = newthread()
      sleep(4)
    end
  }

And then we get...

  START
  DEAD, RESURRECTING
  START
  DEAD, RESURRECTING
  START

(!!) It simply seems that the ruby thread dies inside the eval, and the exception is never raised (?). Has someone an explanation for this ? (found a bug somewhere ?) :slight_smile:

Many thanks by advance for those who'd work on that matter!

Ben

Hi,

it's easy:
$FOO+2 raises NoMethodError < StandardError
while eval(..) raises SyntaxError !< StandardError

empty rescue == rescue StandardError

If you want to catch ALL exceptions, use rescue Exception.

For exceptions hierarchy see

···

On 2/5/07, Benjamin Babut <benjamin@coolsand-tech.fr> wrote:

Hello everyone!

I've encountered a strange issue when coding a tool extending ruby, and
was wondering if I was missing something... I'm working under windows with
the last stable release of ruby (1.8.5-21) and did not find a way to debug
ruby sources to observe the behaviour. This is reproductible with just the
common ruby interpreter. Let me explain the problem :

First let's consider this small script :

        #!/usr/bin/env ruby
        $FOO+2

We get the evident result ($FOO is not defined, an exception's raised) :

        test.rb:2: undefined method `+' for nil:NilClass (NoMethodError)

Then let's consider this small script :

        #!/usr/bin/env ruby
        eval("puts \"foo")

You can see that the string given to puts is malformed, so we get again an
exception :

        (eval):1: compile error (SyntaxError)
        (eval):1: unterminated string meets end of file

Ok. Now let's thread this. Let's consider the following script :

        #!/usr/bin/env ruby
        def newthread()
                toret = Thread.new {
                puts "START"
                begin
                        $FOO+2
                rescue
                        puts("EXCEPTION !!!!!!!!")
                end
                puts "END"
                }
                return toret
        end

        $TESTTHREAD = newthread()
        2.times {
                if(!$TESTTHREAD.alive?) then
                        puts "DEAD, RESURRECTING"
                        $TESTTHREAD = newthread()
                        sleep(4)
                end
        }

We create a thread, provoke an exception, catch it, restart the thread,
and so on twice again. We get the following expected behaviour :

        START
        EXCEPTION !!!!!!!!
        END
        DEAD, RESURRECTING
        START
        EXCEPTION !!!!!!!!
        END
        DEAD, RESURRECTING
        START
        EXCEPTION !!!!!!!!
        END

But now, let's consider this (here comes the issue) : we do exactly the
same thing (we only replace the $FOO+2 line by our malformed eval-puts) :

        #!/usr/bin/env ruby
        def newthread()
                toret = Thread.new {
                puts "START"
                begin
                        eval("puts \"foo")
                rescue
                        puts("EXCEPTION !!!!!!!!")
                end
                puts "END"
                }
                return toret
        end

        $TESTTHREAD = newthread()
        2.times {
                if(!$TESTTHREAD.alive?) then
                        puts "DEAD, RESURRECTING"
                        $TESTTHREAD = newthread()
                        sleep(4)
                end
        }

And then we get...

        START
        DEAD, RESURRECTING
        START
        DEAD, RESURRECTING
        START

(!!) It simply seems that the ruby thread dies inside the eval, and the
exception is never raised (?). Has someone an explanation for this ?
(found a bug somewhere ?) :slight_smile:

Many thanks by advance for those who'd work on that matter!

Hi~

Hello everyone!

I've encountered a strange issue when coding a tool extending ruby, and was wondering if I was missing something... I'm working under windows with the last stable release of ruby (1.8.5-21) and did not find a way to debug ruby sources to observe the behaviour. This is reproductible with just the common ruby interpreter. Let me explain the problem :

First let's consider this small script :

  #!/usr/bin/env ruby
  $FOO+2

We get the evident result ($FOO is not defined, an exception's raised) :

  test.rb:2: undefined method `+' for nil:NilClass (NoMethodError)

Then let's consider this small script :

  #!/usr/bin/env ruby
  eval("puts \"foo")

You can see that the string given to puts is malformed, so we get again an exception :

  (eval):1: compile error (SyntaxError)
  (eval):1: unterminated string meets end of file

Ok. Now let's thread this. Let's consider the following script :

  #!/usr/bin/env ruby
  def newthread()
    toret = Thread.new {
    puts "START"
    begin
      $FOO+2
    rescue
      puts("EXCEPTION !!!!!!!!")
    end
    puts "END"
    }
    return toret
  end

  $TESTTHREAD = newthread()
  2.times {
    if(!$TESTTHREAD.alive?) then
      puts "DEAD, RESURRECTING"
      $TESTTHREAD = newthread()
      sleep(4)
    end
  }

We create a thread, provoke an exception, catch it, restart the thread, and so on twice again. We get the following expected behaviour :

  START
  EXCEPTION !!!!!!!!
  END
  DEAD, RESURRECTING
  START
  EXCEPTION !!!!!!!!
  END
  DEAD, RESURRECTING
  START
  EXCEPTION !!!!!!!!
  END

But now, let's consider this (here comes the issue) : we do exactly the same thing (we only replace the $FOO+2 line by our malformed eval-puts) :

  #!/usr/bin/env ruby
  def newthread()
    toret = Thread.new {
    puts "START"
    begin
      eval("puts \"foo")
    rescue
      puts("EXCEPTION !!!!!!!!")
    end
    puts "END"
    }
    return toret
  end

  $TESTTHREAD = newthread()
  2.times {
    if(!$TESTTHREAD.alive?) then
      puts "DEAD, RESURRECTING"
      $TESTTHREAD = newthread()
      sleep(4)
    end
  }

And then we get...

  START
  DEAD, RESURRECTING
  START
  DEAD, RESURRECTING
  START

(!!) It simply seems that the ruby thread dies inside the eval, and the exception is never raised (?). Has someone an explanation for this ? (found a bug somewhere ?) :slight_smile:

Many thanks by advance for those who'd work on that matter!

Ben

Ben-

  By default ruby doesn't propogate exceptions up out of the threads, if an exception is raised in a thread that thread will just silently dies. To make exceptions in a thread raise to the top level you need to set this in your script:

Thread.abort_on_exception = true

Cheers-

-- Ezra Zygmuntowicz-- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)

···

On Feb 5, 2007, at 2:04 AM, Benjamin Babut wrote:

Hi,

it's easy:
$FOO+2 raises NoMethodError < StandardError
while eval(..) raises SyntaxError !< StandardError

empty rescue == rescue StandardError

If you want to catch ALL exceptions, use rescue Exception.

For exceptions hierarchy see
Ruby | zenspider.com | by ryan davis

Thanks Jan ! Really easy indeed, and it solved my problem :).

Ben