DRbでプロセスをまたがった大域脱出ができるか確認した

結論としてはできなかった。

Rubyのバージョン:

$ ruby -v
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-linux]

確認用のコード:

require 'drb/drb'
require 'drb/unix'
require 'logger'

stdlog = Logger.new(STDOUT)

module Jump
  def ping
    yield('pong')
  end
  module_function :ping

  def occur(tag, value)
    throw(tag, value)
  end
  module_function :occur
end

druby_local_uri = 'drbunix:' + File.expand_path(File.join(File.dirname($0), "druby_local.#{$$}"))
druby_remote_uri = 'drbunix:' + File.expand_path(File.join(File.dirname($0), "druby_remote.#{$$}"))

stdlog.info("start local dRuby service: #{druby_local_uri}")
DRb.start_service(druby_local_uri)

pid = fork{
  stdlog.info("start remote dRuby service: #{druby_remote_uri}")
  DRb.start_service(druby_remote_uri, Jump)
  DRb.thread.join
}

at_exit{
  Process.kill('TERM', pid)
}

drb_remote_obj = DRbObject.new_with_uri(druby_remote_uri)

stdlog.info('wait to start dRuby services')
begin
  t0 = Time.now
  begin
    drb_remote_obj.ping{|r|
      r == 'pong' or raise 'failed ping.'
    }
  rescue DRb::DRbConnError
    if (Time.now - t0 >= 10) then
      raise
    end
    sleep(0.1)
    retry
  end
rescue
  stdlog.fatal('failed to start dRuby services')
  stdlog.fatal($!)
  abort
end

stdlog.info('local jump test')
begin
  r = catch(:foo) {
    Jump.occur(:foo, 'local_jump_test')
    nil
  }
  r == 'local_jump_test' or raise 'failed local jump test.'
rescue
  stdlog.error('NG')
  stdlog.error($!)
else
  stdlog.info('OK')
end

stdlog.info('remote jump test')
begin
  r = catch(:bar) {
    drb_remote_obj.occur(:bar, 'remote_jump_test')
    nil
  }
  r == 'remote_jump_test' or raise 'failed remote jump test.'
rescue
  stdlog.error('NG')
  stdlog.error($!)
else
  stdlog.info('OK')
end

確認結果:

$ ./druby_ipc_jump.rb
I, [2019-04-12T00:25:00.940801 #4402]  INFO -- : start local dRuby service: drbunix:/home/toki/git_work/ruby_examples/druby_local.4402
I, [2019-04-12T00:25:00.962852 #4402]  INFO -- : wait to start dRuby services
I, [2019-04-12T00:25:00.964105 #4404]  INFO -- : start remote dRuby service: drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402
I, [2019-04-12T00:25:01.072707 #4402]  INFO -- : local jump test
I, [2019-04-12T00:25:01.083738 #4402]  INFO -- : OK
I, [2019-04-12T00:25:01.083973 #4402]  INFO -- : remote jump test
E, [2019-04-12T00:25:01.100414 #4402] ERROR -- : NG
E, [2019-04-12T00:25:01.100528 #4402] ERROR -- : uncaught throw :bar (UncaughtThrowError)
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) ./druby_ipc_jump.rb:17:in `throw'
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) ./druby_ipc_jump.rb:17:in `occur'
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) /usr/local/lib/ruby/2.6.0/drb/drb.rb:1635:in `perform_without_block'
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) /usr/local/lib/ruby/2.6.0/drb/drb.rb:1595:in `perform'
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) /usr/local/lib/ruby/2.6.0/drb/drb.rb:1679:in `block (2 levels) in main_loop'
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) /usr/local/lib/ruby/2.6.0/drb/drb.rb:1675:in `loop'
(drbunix:/home/toki/git_work/ruby_examples/druby_remote.4402) /usr/local/lib/ruby/2.6.0/drb/drb.rb:1675:in `block in main_loop'
./druby_ipc_jump.rb:77:in `block in <main>'
./druby_ipc_jump.rb:76:in `catch'
./druby_ipc_jump.rb:76:in `<main>'

RubyUncaughtThrowErrorは大域脱出のtagvalueの値を持つのでハンドリングできそうなものだが実装されてないのかな。