Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Nested BW::HTTP.get requests will make the app crash #151

Closed
padi opened this Issue · 17 comments

6 participants

@padi

When I add another BW::HTTP.get inside the callback of an earlier BW::HTTP.get, the app just closes.

Here's the gist of what I have, removing all extraneous code that can still replicate the scenario:

complete_url = 'http://www.colr.org/json/color/ffba13'
opts={}
BW::HTTP.get(complete_url, opts) do |response|
  puts 'outside block'
  BW::HTTP.get(complete_url, opts) do |resp|
    puts 'inside block'
  end
end

Here's the output when I run rake --trace:

outside block
2012-09-25 14:39:05.449 ipad[52326:f803]  4:in `create_query:': undefined method `[]=' for false:FalseClass (NoMethodError)
    from http.rb:24:in `get:'
    from listings_controller.rb:25:in `block in viewDidLoad'
    from http.rb:260:in `call_delegator_with_response'
    from http.rb:216:in `connectionDidFinishLoading:'
2012-09-25 14:39:05.451 ipad[52326:f803] *** Terminating app due to uncaught exception 'NoMethodError', reason: ' 4:in `create_query:': undefined method `[]=' for false:FalseClass (NoMethodError)
    from http.rb:24:in `get:'
    from listings_controller.rb:25:in `block in viewDidLoad'
    from http.rb:260:in `call_delegator_with_response'
    from http.rb:216:in `connectionDidFinishLoading:'
'
*** First throw call stack:
(0xa19022 0x3a9cd6 0x1b5844 0x2a8b 0x2495 0x1)
terminate called throwing an exception** Execute default
@supermarin
Owner

Thanks for reporting this.

However, I wasn't able to reproduce this, and one new commit that refactored HTTP methods came this morning (3b6f203).

This code:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)

    complete_url = 'http://www.colr.org/json/color/ffba13'
    opts={}
    BW::HTTP.get(complete_url, opts) do |response|
      puts 'outside block'
      BW::HTTP.get(complete_url, opts) do |resp|
        puts 'inside block'
      end
    end

    true
  end
end

gives me this output:

(main)> outside block
inside block

Gemfile:

gem 'bubble-wrap', :git => 'https://github.com/rubymotion/BubbleWrap.git'
@padi

Hi @mneorr,

Thanks for the feedback. It seems that the code above should be working.

I have updated the code that reproduces the same error from a pristine rubymotion app:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    request = lambda {
      complete_url = 'http://www.colr.org/json/color/ffba13'
      opts={}
      BW::HTTP.get(complete_url, opts) do |response|
        puts 'outside block'
        BW::HTTP.get(complete_url, opts) do |resp|
          puts 'inside block'
        end
      end
    }

    BW::HTTP.get('http://www.colr.org/json/color/ffba13') do |response|
      request.call
    end
    true
  end
end

Running with rake --trace:

** Invoke default (first_time)
** Invoke simulator (first_time)
** Invoke build:simulator (first_time)
** Execute build:simulator
** Execute simulator
DYLD_FRAMEWORK_PATH="/Applications/Xcode.app/Contents/Developer/../Frameworks":"/Applications/Xcode.app/Contents/Developer/../OtherFrameworks" /Library/RubyMotion/bin/sim 2 2 5.1 "/Applications/Xcode.app/Contents/Developer" "./build/iPhoneSimulator-5.1-Development/bugtest.app"
(main)> outside block
2012-09-26 12:41:39.058 bugtest[77977:f803]  4:in `create_query:': undefined method `[]=' for false:FalseClass (NoMethodError)
    from http.rb:24:in `get:'
    from app_delegate.rb:8:in `block in application:didFinishLaunchingWithOptions:'
    from http.rb:260:in `call_delegator_with_response'
    from http.rb:216:in `connectionDidFinishLoading:'
** Execute default

Here's my Gemfile:

source :rubygems
gem 'bubble-wrap', '~> 1.0.0', :require => ['bubble-wrap/core', 'bubble-wrap/http']
gem 'rake'

Weird thing is when I move complete_url = 'http://www.colr.org/json/color/ffba13' and
opts={} out of the request lambda. The error disappears. I'm not sure if this is still a BW issue, a RubyMotion issue, or something about ruby lambda that I have missed. Kindly close this issue if this is outside of BW.

This code works now, moving assignments outside the lambda:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    complete_url = 'http://www.colr.org/json/color/ffba13'
    opts={}
    request = lambda {
      BW::HTTP.get(complete_url, opts) do |response|
        puts 'outside block'
        BW::HTTP.get(complete_url, opts) do |resp|
          puts 'inside block'
        end
      end
    }

    BW::HTTP.get('http://www.colr.org/json/color/ffba13') do |response|
      request.call
    end
    true
  end
end

Running rake, I get what I expect.

     Build ./build/iPhoneSimulator-5.1-Development
   Compile ./app/app_delegate.rb
      Link ./build/iPhoneSimulator-5.1-Development/bugtest.app/bugtest
    Create ./build/iPhoneSimulator-5.1-Development/bugtest.dSYM
  Simulate ./build/iPhoneSimulator-5.1-Development/bugtest.app
(main)> outside block
(main)> inside block
@supermarin
Owner

@padi you have a pretty much outdated BubbleWrap version , try with mine Gemfile.

A possible scenario is that you might also be running an older RubyMotion version.
Please try to make sure you run this code on the lastest RM and BW, and if it still persists, I'll take one more look into it.

The create_query method doesn't exist anymore in the BW internally.
You can try renaming to @opts and @complete_url to make sure it isn't a RubyMotion issue.

@padi

I'm using rubymotion 1.24

$ motion -v
1.24

Now I've used the same BW. Here's the Gemfile:

source :rubygems
gem 'bubble-wrap', :git => 'https://github.com/rubymotion/BubbleWrap.git'
gem 'rake'

Here's the trace:

** Invoke default (first_time)
** Invoke simulator (first_time)
** Invoke build:simulator (first_time)
** Execute build:simulator
** Execute simulator
DYLD_FRAMEWORK_PATH="/Applications/Xcode.app/Contents/Developer/../Frameworks":"/Applications/Xcode.app/Contents/Developer/../OtherFrameworks" /Library/RubyMotion/bin/sim 2 2 5.1 "/Applications/Xcode.app/Contents/Developer" "./build/iPhoneSimulator-5.1-Development/bugtest.app"
(main)> outside block
** Execute default //app closes after this without warning or anything
@supermarin
Owner

I can confirm that this code works:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)

    complete_url = 'http://www.colr.org/json/color/ffba13'
    opts={}

    request = lambda {
      BW::HTTP.get(complete_url, opts) do |response|
        puts 'outside block'
        BW::HTTP.get(complete_url, opts) do |resp|
          puts 'inside block'
        end
      end
    }

    BW::HTTP.get('http://www.colr.org/json/color/ffba13') do |response|
      request.call
    end

    true
  end
end

output:

(main)> outside block
(main)> inside block

anybody else can check this?

EDIT: Have you run bundle install after changing the gem source?

@padi

Yes, I did bundle install

The code that doesn't work is the version where the complete_url and opts={} are inside the request lambda, i.e.

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    request = lambda {
      complete_url = 'http://www.colr.org/json/color/ffba13'
      opts={}
      BW::HTTP.get(complete_url, opts) do |response|
        puts 'outside block'
        BW::HTTP.get(complete_url, opts) do |resp|
          puts 'inside block'
        end
      end
    }

    BW::HTTP.get('http://www.colr.org/json/color/ffba13') do |response|
      request.call
    end
    true
  end
end
@supermarin
Owner

Okay, the above one throws an exception.
It's a well known RubyMotion bug, and if you replace complete_url with @complete_url, and opts with @opts , you should be fine.

@supermarin supermarin closed this
@padi

Yeah, figured that out just today... the hard way.

http://blog.blazingcloud.net/2012/07/16/rubymotion-block-scope-bug/

@dmarkow
Collaborator

Yeah, I'm starting to feel like I should never use a local variable anywhere, ever, in a RubyMotion app :(

@supermarin
Owner

+1 ,. I've made a RMBugz app today,.

Not sure if I should implement all those scenarios and release it.
They should definitely fix those bugs, and this app would be a real use-case tester

@siuying
Collaborator

any usage of block and dispatch must be very careful or unexpected bug can happen only on some devices and some environment :(

@supermarin
Owner

does it make sense then, to make a dummy app that we'll just paste our real world examples into?

the purpose of that would be that every RM update should pass that app once the bugs are fixed.

@lrz

Hey guys, please make sure to report these bugs via the tickets system so that we can track them accordingly.

It seems that there is still a memory management bug regarding dynamic (block) variables, I now have a test case for at least one of the scenarios and we will fix it in the next update.

@padi

@lrz done at HipByte/RubyMotion#42. I was just waiting for the confirmation that this is indeed not yet resolved.

@dmarkow
Collaborator

@padi I think @lrz means using RubyMotion's actual ticketing system (from the console, motion support)

@royaltm

I don't know if there is any progress in hunting the "local variable retain bug", but i might have isolated the cases, where retaining works and where the local variable is lost. After some hours of experimentations the repeating pattern emerges. It has something to do with the arity of the method (or the block) that the local variables are created in.

The cases:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:opts)
    ugh
    foo
    hee
    bar
    true
  end

  def foo(dummy=nil)
    fo = "foo"
    fire = proc do
      puts "\nin the foo" # => prints "in the foo"
      puts fo.inspect # => prints "foo"
    end
    NSTimer.scheduledTimerWithTimeInterval(2,target: fire, selector: 'call:', userInfo: nil, repeats: false)
  end

  def hee(*args)
    he = "hee"
    fire = proc do
      puts "\nin the hee" # => prints "in the hee"
      puts he.inspect # => prints "hee"
    end
    NSTimer.scheduledTimerWithTimeInterval(2,target: fire, selector: 'call:', userInfo: nil, repeats: false)
  end

  def bar(&block)
    ba = "bar"
    fire = proc do
      puts "\nin the bar" # => prints "in the bar"
      puts ba.inspect # => prints "bar"
    end
    NSTimer.scheduledTimerWithTimeInterval(2,target: fire, selector: 'call:', userInfo: nil, repeats: false)
  end

  def ugh
    ug = "ugh"
    fire = proc do
      puts "\nin the ugh" # => prints "in the ugh"
      puts ug.inspect # => prints garbage or crashes, ug is gone!
    end
    NSTimer.scheduledTimerWithTimeInterval(2,target: fire, selector: 'call:', userInfo: nil, repeats: false)
  end

end

Surprisingly it seems that the problem with "lost" local variables appears only when the variables are created in the method (or block) with arity == 0. Even &block argument helps.
No matter what the order of the foo, bar, ugh... invocation is. No matter what are the timeouts. It definitely has something to do with the arity of the block or method definition.
It also doesn't matter if the arguments are provided while invoking the method (or block) or if the defaults are used.

As a proof i fixed @padi example providing simple dummy=nil argument to the first lambda.

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    request = lambda { | dummy=nil | # this fixes it <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      complete_url = 'http://www.colr.org/json/color/ffba13'
      opts={}
      BW::HTTP.get(complete_url, opts) do |response|
        puts 'outside block'
        BW::HTTP.get(complete_url, opts) do |resp|
          puts 'inside block'
        end
      end
    }

    BW::HTTP.get('http://www.colr.org/json/color/ffba13') do |response|
      request.call
    end
    true
  end
end

The above code (with the dummy=nil fix) is not crashing anymore.
Please test it and let me know if you can confirm my finding. Then i will ticket it right away to the RMBugz, maybe it will help.

@siuying
Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.