module Await::ThreadExtension

Public Instance Methods

await() { || ... } click to toggle source
# File lib/await.rb, line 6
def await
  # Capture the current context to properly chain this await in
  parent = @deferred
  
  deferred = @deferred = {
    :fiber => Fiber.current
  }

  if (parent)
    parent[deferred] = true
  end

  yield if (block_given?)
  
  # So long as there is at least one outstanding defer block, this fiber
  # must continue to yield.
  while (deferred.size > 1)
    fiber, trigger, block, args = Fiber.yield
    
    deferred.delete(trigger)

    if (block)
      # Always introduce the correct context here by setting the
      # thread-local @deferred instance variable.
      @deferred = deferred
      block.call(*args)
    end
  end
  
  # If this was part of an existing await call then remove the current
  # await operation from the list of pending entries.
  if (parent)
    if (deferred[:fiber] == Fiber.current)
      parent.delete(deferred)
    else
      parent[:fiber].transfer([ Fiber.current, deferred ])
    end
  end

  true
end
defer(&block) click to toggle source
# File lib/await.rb, line 48
def defer(&block)
  deferred = @deferred
  
  trigger = lambda do |*args|
    if (deferred[:fiber] == Fiber.current)
      # Within the same fiber, remove this from the pending list
      deferred.delete(trigger)

      block.call(*args)
    else
      # If this is executing in a different fiber, transfer control back
      # to the original fiber for reasons of continuity.
      deferred[:fiber].transfer([ Fiber.current, trigger, block, args ])
    end
  end
  
  deferred[trigger] = true
  
  trigger
end