dv/redis-semaphore

Stack level too deep after sem.lock(0)

eugenk opened this issue · 5 comments

Running on redis-semaphore, '0.3.0' I get "a stack level too deep" after some basic operations:

 ❯ ruby --version
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]

 ❯ redis-cli flushdb
OK

 ❯ irb
irb(main):001:0> require 'redis-semaphore'
=> true
irb(main):002:0> l = Redis::Semaphore.new(:abc)
=> #<Redis::Semaphore:0x007fc97316a398 @name=:abc, @expiration=nil, @resource_count=1, @stale_client_timeout=nil, @redis=#<Redis client v3.2.2 for redis://127.0.0.1:6379/0>, @use_local_time=nil, @tokens=[]>
irb(main):003:0> l.lock(0)
=> nil
irb(main):004:0> l.locked?
SystemStackError: stack level too deep
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
... 11515 levels...
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:99:in `block in locked?'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `each'
    from /usr/local/opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/redis-semaphore-0.3.0/lib/redis/semaphore.rb:98:in `locked?'
    from (irb):4
    from /usr/local/opt/rbenv/versions/2.3.0/bin/irb:11:in `<main>'irb(main):005:0>

I did some debugging:

In line 73, nil is added to the array @tokens. This causes locked? to recurse indefinitely.

This is because for timeout == 0, we go into the "else" part in line 67 and lpop does not return an array, but only a string ("0").

Then, current_token = token_pair[1] line 72 is not the string that it should be. It is rather nil.

I'm not sure what the expected/intended behaviour is, so I won't create a pull request. I hope this debugging helps you fix the problem.

ujh commented

👍

I face the same issue. This bug makes lock(0) not work at all (it always returns nil).

Any chance this is going to get merged/fixed in a new version soon? I ran into this issue as well... my particular use needs it to return immediately if the semaphore cannot be locked. Another workaround would be to allow fractions of a second... but that does not appear to work either :)

dv commented

Interesting! I'll try to have a look soon and fix this. My apologies for the delay on this, and thanks for putting in the time and debugging the issue!