turboladen / playful

A Ruby implementation of UPnP that works with Ruby >= 1.9.x
MIT License
127 stars 18 forks source link

FiberError when calling ContentDirectory#Browse #14

Open sidoh opened 9 years ago

sidoh commented 9 years ago

I'm using the following adaptation of the example in the Readme:

require 'playful/ssdp'
require 'playful/control_point/device'

EM.run do
  searcher = Playful::SSDP.search 'uuid'

  # Create a deferrable object that can be notified when the device we want
  # has been found and created.
  device_controller = EventMachine::DefaultDeferrable.new

  # This callback will get called when the device_creator callback is called
  # (which is called after the device has been created).
  device_controller.callback do |device|
    p device.service_list.map(&:service_type).inspect
    content_service = device.service_list.reject { |x| !x.service_type.include? 'ContentDirectory' }.first

    raise RuntimeError.new("Couldn't find ContentDirectory service") if content_service.nil?

      p content_service.Browse ObjectID: '0',
                               BrowseFlag: 'BrowseMetadata',
                               Filter: '*',
                               StartingIndex: '0',
                               RequestedCount: '0'
  end

  # Note that you don't have to check for items in the Channel or for when the
  # Channel is empty: EventMachine will pop objects off the Channel as soon as
  # they're put there and stop when there are none left.
  searcher.discovery_responses.pop do |notification|

    # Playful::ControlPoint::Device objects are EventMachine::Deferrables, so you
    # need to define callback and errback blocks to handle when the Device
    # object is done being created.
    device_creator = Playful::ControlPoint::Device.new(ssdp_notification: notification)

    device_creator.errback do
      puts "Failed creating the device."
      exit!
    end

    device_creator.callback do |built_device|
      puts "Device has been created now."

      # This lets the device_controller know that the device has been created,
      # calls its callback, and passes the built device to it.
      device_controller.set_deferred_status(:succeeded, built_device)
    end

    # This actually starts the Device creation process and will call the
    # callback or errback (above) when it's done.
    device_creator.fetch
  end
end

I always get this error:

(eval):11:in `yield': can't yield from root fiber (FiberError)
    from (eval):11:in `post'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/httpi-2.4.1/lib/httpi/adapter/em_http.rb:50:in `block in request'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/httpi-2.4.1/lib/httpi/adapter/em_http.rb:68:in `_request'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/httpi-2.4.1/lib/httpi/adapter/em_http.rb:50:in `request'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/httpi-2.4.1/lib/httpi.rb:161:in `request'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/httpi-2.4.1/lib/httpi.rb:133:in `post'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/savon-2.11.1/lib/savon/operation.rb:94:in `block in call_with_logging'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/savon-2.11.1/lib/savon/request_logger.rb:12:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/savon-2.11.1/lib/savon/request_logger.rb:12:in `log'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/savon-2.11.1/lib/savon/operation.rb:94:in `call_with_logging'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/savon-2.11.1/lib/savon/operation.rb:54:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/savon-2.11.1/lib/savon/client.rb:36:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/playful-0.1.0.alpha.1/lib/playful/control_point/service.rb:280:in `block in define_method_from_action'
    from /home/cmullins/code/me/echo/playground/control_point_test.rb:19:in `block (2 levels) in <top (required)>'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/deferrable.rb:151:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/deferrable.rb:151:in `set_deferred_status'
    from /home/cmullins/code/me/echo/playground/control_point_test.rb:64:in `block (3 levels) in <top (required)>'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/deferrable.rb:151:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/deferrable.rb:151:in `set_deferred_status'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/playful-0.1.0.alpha.1/lib/playful/control_point/device.rb:176:in `block in fetch'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/tick_loop.rb:55:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/tick_loop.rb:55:in `stop'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/em/tick_loop.rb:77:in `block in schedule'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/eventmachine.rb:968:in `call'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/eventmachine.rb:968:in `block in run_deferred_callbacks'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/eventmachine.rb:965:in `times'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/eventmachine.rb:965:in `run_deferred_callbacks'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/eventmachine.rb:187:in `run_machine'
    from /home/cmullins/.rvm/gems/ruby-2.0.0-rc1/gems/eventmachine-1.0.7/lib/eventmachine.rb:187:in `run'
    from /home/cmullins/code/me/echo/playground/control_point_test.rb:4:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

From poking around, it looks like it's related to em-synchrony doing stuff with http requests:

  1. https://github.com/igrigorik/em-synchrony/issues/33
  2. https://github.com/faye/faye/issues/229

If I wrap the code that initiates the SOAP request in a Fiber.new block, it works:

fiber = Fiber.new do
      p content_service.Browse ObjectID: '0',
                               BrowseFlag: 'BrowseMetadata',
                               Filter: '*',
                               StartingIndex: '0',
                               RequestedCount: '0'
end
fiber.resume

Not sure if that's the correct thing to do. Would love to hear if there's a more idiomatic way to solve this.