{"action":"created","issue":{"url":"https://api.github.com/repos/jruby/jruby/issues/2925","labels_url":"https://api.github.com/repos/jruby/jruby/issues/2925/labels{/name}","comments_url":"https://api.github.com/repos/jruby/jruby/issues/2925/comments","events_url":"https://api.github.com/repos/jruby/jruby/issues/2925/events","html_url":"https://github.com/jruby/jruby/issues/2925","id":74961519,"number":2925,"title":"Object became nil","user":{"login":"dzjuck","id":1269260,"avatar_url":"https://avatars.githubusercontent.com/u/1269260?v=3","gravatar_id":"","url":"https://api.github.com/users/dzjuck","html_url":"https://github.com/dzjuck","followers_url":"https://api.github.com/users/dzjuck/followers","following_url":"https://api.github.com/users/dzjuck/following{/other_user}","gists_url":"https://api.github.com/users/dzjuck/gists{/gist_id}","starred_url":"https://api.github.com/users/dzjuck/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dzjuck/subscriptions","organizations_url":"https://api.github.com/users/dzjuck/orgs","repos_url":"https://api.github.com/users/dzjuck/repos","events_url":"https://api.github.com/users/dzjuck/events{/privacy}","received_events_url":"https://api.github.com/users/dzjuck/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-05-10T17:21:13Z","updated_at":"2015-05-14T16:49:42Z","closed_at":null,"body":"I'm trying to add JRuby support to [Hamster gem](https://github.com/hamstergem/hamster), and one spec doesn't work with JRuby:\r\n\r\nSteps to reproduce:\r\n```ruby\r\ngit clone git@github.com:hamstergem/hamster.git\r\ncd hamster\r\nbundle\r\n\r\nrspec ./spec/lib/hamster/list/partition_spec.rb:39\r\n``` \r\n\r\n[Spec code](https://github.com/hamstergem/hamster/blob/25bda8c5af478e9720208972a2183325548106c7/spec/lib/hamster/list/partition_spec.rb#L39)\r\n```ruby\r\n    it \"calls the passed block only once for each item, even with multiple threads\" do\r\n      mutex = Mutex.new\r\n      yielded = [] # record all the numbers yielded to the block, to make sure each is yielded only once\r\n      list = Hamster.iterate(0) do |n|\r\n        sleep(rand / 500) # give another thread a chance to get in\r\n        mutex.synchronize { yielded << n }\r\n        sleep(rand / 500)\r\n        n + 1\r\n      end\r\n      left, right = list.partition(&:odd?)\r\n\r\n      10.times.collect do |i|\r\n        Thread.new do\r\n          # half of the threads will consume the \"left\" lazy list, while half consume\r\n          # the \"right\" lazy list\r\n          # make sure that only one thread will run the above \"iterate\" block at a\r\n          # time, regardless\r\n          if i % 2 == 0\r\n            left.take(100).sum.should == 10000\r\n          else\r\n            right.take(100).sum.should == 9900\r\n          end\r\n        end\r\n      end.each(&:join)\r\n    end\r\n```\r\n\r\nerror happens in one of two strings:\r\n```ruby\r\n            left.take(100).sum.should == 10000\r\n            right.take(100).sum.should == 9900\r\n```\r\n\r\n[Code where happens error](https://github.com/hamstergem/hamster/blob/25bda8c5af478e9720208972a2183325548106c7/lib/hamster/list.rb#L1299):\r\n```ruby\r\n  class Partitioned < Realizable\r\n    def initialize(partitioner, buffer, mutex)\r\n      super()\r\n      @partitioner, @buffer, @mutex = partitioner, buffer, mutex\r\n    end\r\n\r\n    def realize\r\n      @mutex.synchronize do\r\n        return if @head != Undefined # another thread got ahead of us\r\n        while true\r\n          if !@buffer.empty?\r\n            @head = @buffer.shift\r\n            @tail = Partitioned.new(@partitioner, @buffer, @mutex)\r\n            # don't hold onto references\r\n            # tail will keep references alive until end of list is reached\r\n            @partitioner, @buffer, @mutex = nil, nil, nil\r\n            return\r\n          elsif @partitioner.done?\r\n            @head, @size, @tail = nil, 0, self\r\n            @partitioner, @buffer, @mutex = nil, nil, nil # allow them to be GC'd\r\n            return\r\n          else\r\n            @partitioner.next_item\r\n          end\r\n        end\r\n      end\r\n    end\r\n  end\r\n```\r\n\r\nError happens on string:\r\n```ruby\r\n      @mutex.synchronize do\r\n```\r\n\r\n```ruby\r\n     Failure/Error: left.take(100).sum.should == 10000\r\n     NoMethodError:\r\n       undefined method `synchronize' for nil:NilClass\r\n```\r\n\r\nA couple of words about what happens here:\r\n* spec creates lazy list, which is iterated over by tail recursion in 10 threads.\r\n* @mutex is created in parent lazy list and is used in all descendant lists\r\n* Partition#realize is called exactly once per object \r\n\r\nError doesn't happen in MRI and Rubinius.\r\n\r\nIt is possible to count errors, if we add 'rescue' to the end of Partitioned#realize:\r\n```ruby\r\n    rescue NoMethodError => e\r\n      puts \"!!! ERROR NO_METHOD_ERROR !!! @mutex: #{@mutex.object_id}\"\r\n```\r\n\r\nOn my laptop error count is 4 in average.\r\n\r\nIf in Partitioned#realize\r\n```ruby\r\n          if !@buffer.empty?\r\n            @head = @buffer.shift\r\n            @tail = Partitioned.new(@partitioner, @buffer, @mutex)\r\n            # don't hold onto references\r\n            # tail will keep references alive until end of list is reached\r\n            @partitioner, @buffer, @mutex = nil, nil, nil\r\n            return\r\n```\r\nwe remove @mutex = nil\r\n```ruby\r\n          if !@buffer.empty?\r\n            @head = @buffer.shift\r\n            @tail = Partitioned.new(@partitioner, @buffer, @mutex)\r\n            # don't hold onto references\r\n            # tail will keep references alive until end of list is reached\r\n            @partitioner, @buffer = nil, nil\r\n            return\r\n```\r\nerror stops to happen.\r\n\r\nPossibly it's a bug of garbage collector.\r\nI don't understand what exactly triggers this error. I tried several times to make independent example to reproduce this bug - without success. If anyone has any idea how to make it - i will implement it gladly.\r\n\r\nReproduced in both JRuby versions - 1.7.19 and 9000.\r\n\r\nThank you.\r\n"},"comment":{"url":"https://api.github.com/repos/jruby/jruby/issues/comments/102100654","html_url":"https://github.com/jruby/jruby/issues/2925#issuecomment-102100654","issue_url":"https://api.github.com/repos/jruby/jruby/issues/2925","id":102100654,"user":{"login":"alexdowad","id":1067359,"avatar_url":"https://avatars.githubusercontent.com/u/1067359?v=3","gravatar_id":"","url":"https://api.github.com/users/alexdowad","html_url":"https://github.com/alexdowad","followers_url":"https://api.github.com/users/alexdowad/followers","following_url":"https://api.github.com/users/alexdowad/following{/other_user}","gists_url":"https://api.github.com/users/alexdowad/gists{/gist_id}","starred_url":"https://api.github.com/users/alexdowad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexdowad/subscriptions","organizations_url":"https://api.github.com/users/alexdowad/orgs","repos_url":"https://api.github.com/users/alexdowad/repos","events_url":"https://api.github.com/users/alexdowad/events{/privacy}","received_events_url":"https://api.github.com/users/alexdowad/received_events","type":"User","site_admin":false},"created_at":"2015-05-14T16:49:42Z","updated_at":"2015-05-14T16:49:42Z","body":"I believe this is a threading bug in the `hamster` gem, which was uncovered when running the specs on JRuby, simply because JRuby allows greater concurrency."}}