Module: Abstractivator::FiberDefer

Defined in:
lib/abstractivator/fiber_defer.rb

Overview

Provides a pair of functions for handling long-running requests in a thread pool. Uses fibers to maintain a somewhat normal coding style (hides explicit continuations). with_fiber_defer defines the lexical scope of all work being done. fiber_defer defines the portion of the work to be done in the worker thread. Control passes from the calling thread, to the worker thread, and back to the calling thread. The code is capable of propagating thread variables (e.g., Mongoid::Threaded.database_override) across these thread/fiber transitions. See EventMachine::defer for more information.

Constant Summary collapse

ROOT_FIBER =
Fiber.current

Instance Method Summary collapse

Instance Method Details

#fiber_defer(thread_var_guard = nil, &action) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/abstractivator/fiber_defer.rb', line 40

def fiber_defer(thread_var_guard=nil, &action)
  # we start out in the caller thread
  inherited_guard_proc = Thread.current[:fiber_defer_guard_proc]
  raise 'fiber_defer cannot be nested within fiber_defer' if Thread.current[:inside_fiber_defer]
  raise 'fiber_defer must be called within a with_fiber_defer block' unless inherited_guard_proc
  raise 'fiber_defer must be passed an action to defer (the block)' unless action
  local_guard_proc = make_guard_proc(thread_var_guard)
  guard_proc = proc do
    inherited_guard_proc.call
    local_guard_proc.call
  end
  begin
    basic_fiber_defer do
      # in the background thread now
      begin
        Thread.current[:inside_fiber_defer] = true
        guard_proc.call
        action.call
      ensure
        Thread.current[:inside_fiber_defer] = false
      end
    end
  ensure
    # back in the caller thread
    guard_proc.call
  end
end

#mongoid_fiber_defer(&action) ⇒ Object



68
69
70
71
72
73
# File 'lib/abstractivator/fiber_defer.rb', line 68

def mongoid_fiber_defer(&action)
  thread_vars = {
    Mongoid::Threaded.database_override => proc { |db| Mongoid.override_database(db) }
  }
  fiber_defer(thread_vars, &action)
end

#with_fiber_defer(thread_var_guard = nil, &block) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/abstractivator/fiber_defer.rb', line 22

def with_fiber_defer(thread_var_guard=nil, &block)
  raise 'this method requires an eventmachine reactor to be running' unless EM.reactor_running?
  raise 'with_fiber_defer cannot be nested within with_fiber_defer' if Thread.current[:fiber_defer_guard_proc]
  raise 'with_fiber_defer cannot be nested within fiber_defer' if Thread.current[:inside_fiber_defer]
  return unless block
  guard_proc = make_guard_proc(thread_var_guard)
  f = Fiber.new do
    guard_proc.call
    begin
      Thread.current[:fiber_defer_guard_proc] = guard_proc # make available to fiber_defer calls
      block.call
    ensure
      Thread.current[:fiber_defer_guard_proc] = nil
    end
  end
  f.resume
end