module Empathy

This module provides a shim between using standard ruby Threads and the thread-like behaviour for Fibers provided by classes in the {Empathy::EM} namespace.

# For the Empathy::EM classes to be available
# you must first load EventMachine
'require eventmachine'

'require empathy'

t = Empathy::Thread.new() do
    # "t" is a standard ::Thread
    # ...something...
end

EventMachine.run do
   t = Empathy::Thread.new() do
        # "t" is a ::Empathy::EM::Thread
        # which wraps a ::Fiber
   end
end

# Outside of event machine
t = Empathy::Thread.new() do
    # "t" is a raw ::Thread
end

#Code using Empathy that may be used in both Fiber or Thread contexts
#should take care to rescue Empathy::ThreadError

def maybe_em_method
   # ...
rescue Empathy::ThreadError
   # ... will rescue either ::FiberError or ::ThreadError
end

Constants

REAL_FIBERS
ROOT_FIBER
VERSION

Public Class Methods

empathise(*modules) click to toggle source

Create alias constants in each of the supplied modules so that code witin those modules will use modules from the Empathy namespace instaad of the native ruby ones

Also monkey patches {Object} to provide EM safe Kernel methods @param [Array<Module>] modules @return [void]

# File lib/empathy.rb, line 85
def self.empathise(*modules)
  modules.each { |m| map_classes(m, self) }
end
event_machine?() click to toggle source

Are we running in the EventMachine reactor thread

For JRuby or other interpreters where fibers are implemented with threads this will return true if the reactor is running and the code is called from any fiber other than the root fiber

# File lib/empathy.rb, line 145
def self.event_machine?
  @loaded ||= false
  @loaded && EventMachine.reactor_running? &&
    ( EventMachine.reactor_thread? || (!REAL_FIBERS && ROOT_FIBER != Fiber.current))
end
map_classes(into,from) click to toggle source

@private

# File lib/empathy.rb, line 90
def self.map_classes(into,from)
  # Make Object reactor aware
  require 'empathy/object'
  @empathic_classes.each do |cname|
    case cname
    when Hash
      cname.each { |cn,replace_class| replace_class_constant(into,cn,replace_class) }
    else
      replace_class_constant(into,cname,from.const_get(cname))
    end
  end
  into.instance_variable_set(:@empathised,from)
end
reload() click to toggle source

Specifically try to enable use of Eventmachine if it is now available

# File lib/empathy.rb, line 127
def self.reload()
  @loaded ||= false
  if !@loaded && defined?(EventMachine)
    require 'empathy/em/thread.rb'
    require 'empathy/em/monitor.rb'
    @loaded = true
  end
  return @loaded
end
run() { || ... } click to toggle source

Start EventMachine and run reactor block within a surrounding Empathy::EM::Thread (Fiber). The reactor loop is terminated when the supplied block finishes @return the value of the block

# File lib/empathy.rb, line 60
def self.run
  reload()
  exception = nil
  value = nil
  EventMachine.run do
    EM::Thread.new do
      begin
        value = yield
      rescue Exception => ex
        exception = ex
      ensure
        EventMachine.stop
      end
    end
  end
  raise exception if exception
  value
end

Private Class Methods

create_delegate_module(cname,*methods) click to toggle source

Create a module under the Empathy namespace that delegates class methods (potentially including new) to either the top level class, or to its equivalent under the {Empathy::EM} namespace based on being in/out of the reactor

Library authors can use this to define an eventmachine aware replacement class for their own purposes @example

class MyBinding
  #...code that does not use event machine
end

module Empathy
  module EM
    class MyBinding
      # ... code that uses event machine ...
    end
  end

  create_delegate_module('MyBinding',:new, :my_class_method)
end

@visibility public @param [String] cname name of the top level class or module to delegate, there must

also be a class with the same name in the Empathy::EM namespace

@param [Array<Sumbol>] methods names of class/module methods to delegate

# File lib/empathy.rb, line 178
def self.create_delegate_module(cname,*methods)
  mod = Module.new
  native_mod = Object.const_get(cname)
  em_mod = Empathy::EM.const_get(cname)
  self.const_set(cname,mod)
  methods.each do |m|
    mod.define_singleton_method(m) do |*args,&block|
      delegate = Empathy.event_machine? ? em_mod : native_mod
      delegate.send(m,*args,&block)
    end
  end
  @empathic_classes << cname unless cname == 'Kernel'
end
replace_class_constant(into,cname,replace_class) click to toggle source
# File lib/empathy.rb, line 105
def self.replace_class_constant(into,cname,replace_class)
  if into != Object && into.const_defined?(cname,false)
     existing_const = into.const_get(cname)
     if !existing_const.is_a?(Module) || existing_const.name.start_with?(into.name)
       warn "mmpathy: Skipping replacement of #{into.name}::#{cname}"
       return nil
     end
  end

  warn "empathy: Defined fake class constant #{into}::#{cname} => #{replace_class.name}"
  into.const_set(cname,replace_class)
end