ibc / AsyncEngine

Ruby asynchronous event driven framework on top of libuv
6 stars 1 forks source link

AE.handle_exception(e = Fixnum: 8) #4

Closed ibc closed 12 years ago

ibc commented 12 years ago

Running this code and pressing Ctrl+C:

loop do
  t = Thread.new {  AE.run { AE.add_periodic_timer(0.05){printf "."} }  }

  sleep 0.05
  t.kill
  sleep 0.01
end

causes:

WARN: AE.handle_exception(e = Fixnum: 8)
./bugs.rb:69:in `sleep': Interrupt

Fixnum????

Also, test_basic.rb contains something like this (simplified):

AE.set_exception_handler {|e| puts "AE.exception_handler rescues #{e.class}: #{e}" }

assert_nothing_raised do
  AE.run { qweqwe }
  AE.run { AE.next_tick { uhhhh } }
  AE.run { AE.add_timer(0) { uhhhh } }
end

All those assert pass the test. The first and second ones perfectly, but the third one makes Ruby TestUnit to fail (rake test --trace):

AE.exception_handler rescues NameError: undefined local variable or method `uhhhh' for #
...............
Finished in 0.192000 seconds.

16 tests, 73 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 12359
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.9.1 -I"lib:test" -I"/var/li...]
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils.rb:53:in `block in create_shell_runner'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils.rb:45:in `call'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils.rb:45:in `sh'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils_ext.rb:39:in `sh'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils.rb:82:in `ruby'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils_ext.rb:39:in `ruby'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/testtask.rb:99:in `block (2 levels) in define'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/file_utils_ext.rb:60:in `verbose'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/testtask.rb:98:in `block in define'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:205:in `call'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:205:in `block in execute'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:200:in `each'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:200:in `execute'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:158:in `block in invoke_with_call_chain'
/usr/lib/ruby/1.9.1/monitor.rb:201:in `mon_synchronize'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:151:in `invoke_with_call_chain'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/task.rb:144:in `invoke'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:116:in `invoke_task'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:94:in `block (2 levels) in top_level'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:94:in `each'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:94:in `block in top_level'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:133:in `standard_exception_handling'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:88:in `top_level'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:66:in `block in run'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:133:in `standard_exception_handling'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/lib/rake/application.rb:63:in `run'
/var/lib/gems/1.9.1/gems/rake-0.9.2.2/bin/rake:33:in `'
/usr/local/bin/rake:19:in `load'
/usr/local/bin/rake:19:in `
'
ibc commented 12 years ago

Without Ruby assert stuff it does not occur:

AE.set_exception_handler {|e| puts "AE.exception_handler rescues #{e.class}: #{e}" }

AE.run { AE.next_tick { uhhhh } }

causes:

AE.exception_handler rescues NameError: undefined local variable or method `uhhhh' for main:Object
ibc commented 12 years ago

And within test_basic.rb, it seems that the captured exception is OK (NameError)...

ibc commented 12 years ago

Fixed.

ibc commented 12 years ago

Unfortunatelly it occurs again after baf1fa3ed8b2ab85c53d47a3e11d1ddf0fc00dfc, but just in the first usecase above.

ibc commented 12 years ago

Humm, this could occur when using Thread#kill since it seems that it's not "rescue-able": http://www.ruby-forum.com/topic/4402523

Yes, it is!:

irb(main):028:0> t = Thread.new { AE.run { AE.add_periodic_timer(1) { printf "." } } }
#
irb(main):029:0> t.kill
AE_TRACE2: asyncengine_ruby.c:128:ae_ubf
#
*** ae_ubf_uv_async_callback(): AE_status: 2, AE_uv_loop: 140126363202112
AE_WARN: ae_handle_common.c:217:execute_function_with_glv_and_rb_protect::  ************ exception_tag == 8
/home/ibc/Proyectos/AsyncEngine/lib/asyncengine.rb:79:in `raise': exception class/object expected (TypeError)
        from /home/ibc/Proyectos/AsyncEngine/lib/asyncengine.rb:79:in `block in run'
        from :10:in `synchronize'
        from /home/ibc/Proyectos/AsyncEngine/lib/asyncengine.rb:64:in `run'
        from (irb):28:in `block in irb_binding'
ibc commented 12 years ago

The "issue" is reported in http://bugs.ruby-lang.org/issues/6575. Probably it's not a bug so in case rb_errinfo() returns != Exception object, I should not attempt to raise it.

ibc commented 12 years ago

I must assume that rb_errinfo() returns a Fixnum (instead of an Exception object) when the thread has been killed with Thread#kill. If I get another explanation I will reopen the issue.

ibc commented 12 years ago

I've found something in ruby sources:

thread.c:

static void
rb_threadptr_execute_interrupts_common(rb_thread_t *th)
{
  [...]

  /* exception from another thread */
  if (th->thrown_errinfo) {
    VALUE err = th->thrown_errinfo;
    th->thrown_errinfo = 0;
    thread_debug("rb_thread_execute_interrupts: %"PRIdVALUE"\n", err);

    if (err == eKillSignal || err == eTerminateSignal) {
      th->errinfo = INT2FIX(TAG_FATAL);
      TH_JUMP_TAG(th, TAG_FATAL);

and in eval_intern.h:

  RUBY_TAG_FATAL        = 0x8,
  #define TAG_FATAL RUBY_TAG_FATAL

So, there is my Fixnum 8 :)

ibc commented 12 years ago

So it seems a bug in Ruby, fixed in trunk: http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/35622

ibc commented 12 years ago

It's not a Ruby bug (regardless it makes no lot of sense...). It seems this explains it: http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/35625

So the way in which AE handles this issue is by replacing the Fixnum by an Interrupt exception.