ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:72149] [Ruby trunk - Bug #11822] [Open] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
@ 2015-12-15 16:05 ` headius
  2015-12-15 16:58 ` [ruby-core:72150] [Ruby trunk - Bug #11822] " headius
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-15 16:05 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been reported by Charles Nutter.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72150] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
  2015-12-15 16:05 ` [ruby-core:72149] [Ruby trunk - Bug #11822] [Open] Semantics of Queue#pop after close are wrong headius
@ 2015-12-15 16:58 ` headius
  2015-12-15 17:48 ` [ruby-core:72152] " headius
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-15 16:58 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Charles Nutter.


=begin
Additional thoughts on this...

I'm not sure the semantics of these queues are compatible with actual parallel execution as in JRuby for a few reasons.

NON-ATOMICITY OF STATE

Setting a closed bit *and* waking all waiting consumers cannot be done atomically. This allows the closed bit to be set *after* another thread has checked it but *before* that thread has inserted an item into the queue. This in turn could cause all waiting threads to wake, receive a nil result, and not see the subsequently-pushed value.

Pseudo-code that represents current MRI impl:

=end
class Queue
  def close
    @closed = true
    notify_waiting_threads
  end

  def push(value)
    raise ClosedQueueError if closed?
    @queue << value
  end
end
=begin

An implementation that can run these pieces of code in parallel can not guarantee the atomicity of closing and notifying waiting threads. The result would be that all waiters might return nil but there's still elements in the queue.

NOTIFICATION OF WAITING THREADS ON CLOSE

It is also not possible to atomically notify all threads on close. More pseudo-code in addition to the above:

=end
class Queue
  def pop
    return nil if closed?
    return @queue.pop if @queue.size != 0
    wait_for_push
  end
end
=begin

Since it is not possible in a parallel implementation to set the closed bit *and* notify all threads atomically, it's possible for a thread to get stuck waiting forever if the following order happens:

1. Thread A checks if the queue is closed? in Queue#pop. It is not, so it proceeds.
2. Thread A checks if the queue is empty. It is empty, so it proceeds.
3. Thread B closes the queue and sets the closed bit and notifies waiting threads. At this point, no threads are waiting.
4. Thread A proceeds to wait on the queue.

Under parallel execution, it is not possible to guarantee no threads will be blocking on the queue after Queue#close has been called given the current semantics of Queue.

I will think on both of these for a while, but I'd also appreciate some input if I've missed a possible solution.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55561

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72152] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
  2015-12-15 16:05 ` [ruby-core:72149] [Ruby trunk - Bug #11822] [Open] Semantics of Queue#pop after close are wrong headius
  2015-12-15 16:58 ` [ruby-core:72150] [Ruby trunk - Bug #11822] " headius
@ 2015-12-15 17:48 ` headius
  2015-12-15 17:52 ` [ruby-core:72155] " headius
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-15 17:48 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Charles Nutter.


FWIW, JRuby has always supported a Queue#shutdown! method that *only* set a shutdown bit because of the problems of setting a bit and safely waking all consumers.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55563

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72155] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (2 preceding siblings ...)
  2015-12-15 17:48 ` [ruby-core:72152] " headius
@ 2015-12-15 17:52 ` headius
  2015-12-15 23:47 ` [ruby-core:72164] " ko1
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-15 17:52 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Charles Nutter.


Also worth noting that java.util.concurrent's BlockingQueue implementations do not support any of the following for the same reasons as above:

* Closing or shutting down a queue
* Notifying all threads waiting on a queue that it is no longer available

The justification is that shutdown semantics for threads waiting on a queue varies too greatly to have a single consistent semantic. Some code will want a "closed" queue to cause all waiters to raise an error, others may want them to all receive a null value, still others will want them to receive a poison pill indicating the queue is done. Basically, the expectation is that if user code sets up threads to wait on a queue, user code should also handle notifying them that the queue is no longer available for use.

I agree.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55565

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72164] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (3 preceding siblings ...)
  2015-12-15 17:52 ` [ruby-core:72155] " headius
@ 2015-12-15 23:47 ` ko1
  2015-12-16  7:39 ` [ruby-core:72178] " funny.falcon
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: ko1 @ 2015-12-15 23:47 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Koichi Sasada.

Description updated

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55575

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72178] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (4 preceding siblings ...)
  2015-12-15 23:47 ` [ruby-core:72164] " ko1
@ 2015-12-16  7:39 ` funny.falcon
  2015-12-18  5:32 ` [ruby-core:72357] " ko1
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: funny.falcon @ 2015-12-16  7:39 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Yura Sokolov.


Charles, closing queue only prevents adding new elements to. It should not delete already added items.

Look at Golang's channels, they behaves same way. It is really most useful behaviour.

Deleting already added items would be very unfriendly behaviour. 

Perhaps, you complain about documentation? It should be clearer about actual way.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55592

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72357] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (5 preceding siblings ...)
  2015-12-16  7:39 ` [ruby-core:72178] " funny.falcon
@ 2015-12-18  5:32 ` ko1
  2015-12-18 18:41 ` [ruby-core:72369] " email
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: ko1 @ 2015-12-18  5:32 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Koichi Sasada.

Assignee set to Koichi Sasada

# Semantics

I'm not sure all I can understand, but Queue#close does not remove remaining items as Yura said.

(on 2.3) Queue#close is only for simple way for the following code:

```ruby
# without Queue#close

q = Queue.new
3.times{
  Thread.new{ # workers
    while e = q.pop
      work_with(e)
    end
  }
}

q.push work1
q.push work2
q.push work3
q.push work4
3.times{
  q.push nil
}
```

```ruby
# with Queue#close

q = Queue.new
3.times{
  Thread.new{ # workers
    while e = q.pop
      work_with(e)
    end
  }
}

q.push work1
q.push work2
q.push work3
q.push work4
q.close # simplified
```

Suggestions about documentation are welcome.

# Atomicity

I think it is possible to implement in atomic with appropriate locks. For example, we can make this implementation on pthread libraries.
However, such locks can be affect performance, especially on well-optimized lock-free queue implementations such as Java has.

I can't decide the problem is critical or not.
We have no time for Ruby 2.3 to prove it.

So I can remove Queue#close and continue discussion for ruby 2.4 if someone (Charles) says it should be removed.

Charles, what do you think?


----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55650

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72369] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (6 preceding siblings ...)
  2015-12-18  5:32 ` [ruby-core:72357] " ko1
@ 2015-12-18 18:41 ` email
  2015-12-19  1:39 ` [ruby-core:72372] " ko1
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: email @ 2015-12-18 18:41 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Petr Chalupa.


Could you clarify for me following cases: What does pop do when called on empty closed queue? What does close do about already blocked threads when it's empty? 
The documentation does not specifies this.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55660

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72372] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (7 preceding siblings ...)
  2015-12-18 18:41 ` [ruby-core:72369] " email
@ 2015-12-19  1:39 ` ko1
  2015-12-19 13:37 ` [ruby-core:72380] " email
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: ko1 @ 2015-12-19  1:39 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Koichi Sasada.


Petr Chalupa wrote:
> Could you clarify for me following cases: What does pop do when called on empty closed queue? What does close do about already blocked threads when it's empty? 
> The documentation does not specifies this.

> What does pop do when called on empty closed queue?

returns nil.

> What does close do about already blocked threads when it's empty? 

I assume 'already blocked threads' are consumer threads.
They are resumed, and get nil.

You can verify with code.

```ruby
q = Queue.new
q.close
p q.pop
```

```ruby
q = Queue.new
Thread.new{
  sleep 1
  q.close
}
p q.pop
```



----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55663

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72380] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (8 preceding siblings ...)
  2015-12-19  1:39 ` [ruby-core:72372] " ko1
@ 2015-12-19 13:37 ` email
  2015-12-19 14:37 ` [ruby-core:72382] " headius
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: email @ 2015-12-19 13:37 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Petr Chalupa.


Thank you for explanation. So nil can mean a normal nil pushed to the queue or closed queue, that might be error prone. As Charles mentioned there are many possible behaviors users might want when queue is closed. I would suggest to make this configurable on queue initialization. 

~~~ ruby
Queue.new on_close: nil # pops nil
Queue.new on_close: :poison_pill # pops symbol :poison_pill
CLOSED = Object.new
Queue.new on_close: CLOSED # pops CLOSED
Queue.new on_close: ClosedQueueError # raises exception of supplied class
~~~

Raising exception seems unnecessary to me, since close is normal feature of the queue not an exceptional state (this is subjective though so including in the example too).

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55670

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72382] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (9 preceding siblings ...)
  2015-12-19 13:37 ` [ruby-core:72380] " email
@ 2015-12-19 14:37 ` headius
  2015-12-19 15:01 ` [ruby-core:72383] " email
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-19 14:37 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Charles Nutter.


tldr: Atomicity can be achieved with full locking, but it prevents Queue from being lock-free in the future. #pop should have a way to indicate the queue is closed other than just returning nil. I don't see anything that prevents the current impl of #close from going into 2.3.

Atomicity
-----------

It is possible to implement these operations with a hard lock, but my concern is that it prevents us from using more efficient lock-free Queue implementations in the future.

I have reimplemented JRuby's Queue to mimic a GVL by locking around all operations, and it is passing tests. Performance seems fine, but there were other changes that muddle measurement a bit.

Queue#pop behavior on a closed queue
----------------------------------------------

I do have concerns about #pop returning nil on a closed queue, but it is not an implementation challenge...just an API challenge. The concern is this: there's no indication from #pop that the queue is closed, since nil is a valid value to push into a queue. Or put another way: it will no longer be safe to have nil be a regular value in a queue, since it could now mean the queue is closed. You will have to check #closed? every time.

Perhaps there should be a way to query if the queue is closed *and* pop at the same time? Go provides a multiple assignment form of receive:

x, ok = <-ch

where "ok" receives a boolean indicating that the queue is closed.

We don't want to make pop always return multiple values, so here's a couple ideas for how we might do this in Ruby:

(Queue#pop already takes a boolean value to indicate if it should be nonblocking, so overloading Queue#pop is not an option)

```ruby
q.pop!([nonblock]) # raises exception on a closed queue
q.pop?(value [, nonblock]) # returns value if queue is closed (or would block?), rather than nil
```

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55671

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72383] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (10 preceding siblings ...)
  2015-12-19 14:37 ` [ruby-core:72382] " headius
@ 2015-12-19 15:01 ` email
  2015-12-19 15:27 ` [ruby-core:72386] " headius
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: email @ 2015-12-19 15:01 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Petr Chalupa.


I like this better to make it configurable per pop operation.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55672

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72386] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (11 preceding siblings ...)
  2015-12-19 15:01 ` [ruby-core:72383] " email
@ 2015-12-19 15:27 ` headius
  2015-12-19 15:29 ` [ruby-core:72387] " headius
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-19 15:27 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Charles Nutter.


Summarizing some discussion with Koichi on #ruby-core:

* Current JRuby Queue uses Java's LinkedBlockingQueue and ArrayBlockingQueue, which both are locking implementations. There are lock-free queue implementations on JVM but they do not support blocking.
* Performance of the fully-locked impl of Queue from MRI 2.3 appears to be as good or better than JRuby's existing impls based on Java LBQ and ABQ for single and multi-threaded benchmarks. This is likely because the existing impls were also fully locked *and* JRuby had to implement num_waiters separately.
* The API proposed for #close, #push, and #pop for 2.3 is accepted by JRuby since there appears to be no performance implication right now.

So we should bump this to to Ruby 2.4 to address the inability to check for closed? and pop at the same time, as in Go. I believe that is the only remaining issue.

This can go forward in 2.3.

For reference, here's what OpenJDK lock-free queues are based upon: http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf

We may want to add a lock-free queue implementation to either Ruby or concurrent-ruby in the future.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55674

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72387] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (12 preceding siblings ...)
  2015-12-19 15:27 ` [ruby-core:72386] " headius
@ 2015-12-19 15:29 ` headius
  2015-12-20  8:54 ` [ruby-core:72408] " funny.falcon
  2017-01-31  7:12 ` [ruby-core:79334] [Ruby trunk Bug#11822][Closed] " ko1
  15 siblings, 0 replies; 17+ messages in thread
From: headius @ 2015-12-19 15:29 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Charles Nutter.


Clarifying my statements above:

"So we should bump this to to Ruby 2.4 to address the inability to check for closed? and pop at the same time, as in Go."

I mean we should discuss further API improvements like Queue#pop! or pop? for Ruby 2.4.

"This can go forward in 2.3."

I mean current #close, #closed?, #push, and #pop changes can go forward as they are implemented on trunk today.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55676

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:72408] [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (13 preceding siblings ...)
  2015-12-19 15:29 ` [ruby-core:72387] " headius
@ 2015-12-20  8:54 ` funny.falcon
  2016-01-25 18:21   ` [ruby-core:73429] " Andrew Vit
  2017-01-31  7:12 ` [ruby-core:79334] [Ruby trunk Bug#11822][Closed] " ko1
  15 siblings, 1 reply; 17+ messages in thread
From: funny.falcon @ 2015-12-20  8:54 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Yura Sokolov.


I like optional arg to pop:
Either `pop?` or `pop(on_close: value)`

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-55696

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: Koichi Sasada
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:73429] Re: [Ruby trunk - Bug #11822] Semantics of Queue#pop after close are wrong
  2015-12-20  8:54 ` [ruby-core:72408] " funny.falcon
@ 2016-01-25 18:21   ` Andrew Vit
  0 siblings, 0 replies; 17+ messages in thread
From: Andrew Vit @ 2016-01-25 18:21 UTC (permalink / raw)
  To: ruby-core

On 2015-12-20 12:54 AM, funny.falcon@gmail.com wrote:
> I like optional arg to pop:
> Either `pop?` or `pop(on_close: value)`

Another possibility is a default proc similar to how Hash#fetch 
interface works.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [ruby-core:79334] [Ruby trunk Bug#11822][Closed] Semantics of Queue#pop after close are wrong
       [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
                   ` (14 preceding siblings ...)
  2015-12-20  8:54 ` [ruby-core:72408] " funny.falcon
@ 2017-01-31  7:12 ` ko1
  15 siblings, 0 replies; 17+ messages in thread
From: ko1 @ 2017-01-31  7:12 UTC (permalink / raw)
  To: ruby-core

Issue #11822 has been updated by Koichi Sasada.

Status changed from Open to Closed

Now close this issue and please file another ticket if someone need to change it.

----------------------------------------
Bug #11822: Semantics of Queue#pop after close are wrong
https://bugs.ruby-lang.org/issues/11822#change-62752

* Author: Charles Nutter
* Status: Closed
* Priority: Normal
* Assignee: Koichi Sasada
* Target version: 
* ruby -v: 
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Current test/ruby/thread/test_queue.rb test_close has the following assertion that seems wrong to me:

```ruby
  def test_close
    [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
      q = qcreate.call
      assert_equal false, q.closed?
      q << :something
      assert_equal q, q.close
      assert q.closed?
      assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
      assert_equal q.pop, :something  # <<< THIS ONE
      assert_nil q.pop
      assert_nil q.pop
      # non-blocking
      assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
    end
  end
```

Once a queue is closed, I don't think it should ever return a result anymore. The queue should be cleared and pop should always return nil.

In r52691, ko1 states that "deq'ing on closed queue returns nil, always." This test does not match that behavior.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2017-01-31  6:43 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <redmine.issue-11822.20151215160530@ruby-lang.org>
2015-12-15 16:05 ` [ruby-core:72149] [Ruby trunk - Bug #11822] [Open] Semantics of Queue#pop after close are wrong headius
2015-12-15 16:58 ` [ruby-core:72150] [Ruby trunk - Bug #11822] " headius
2015-12-15 17:48 ` [ruby-core:72152] " headius
2015-12-15 17:52 ` [ruby-core:72155] " headius
2015-12-15 23:47 ` [ruby-core:72164] " ko1
2015-12-16  7:39 ` [ruby-core:72178] " funny.falcon
2015-12-18  5:32 ` [ruby-core:72357] " ko1
2015-12-18 18:41 ` [ruby-core:72369] " email
2015-12-19  1:39 ` [ruby-core:72372] " ko1
2015-12-19 13:37 ` [ruby-core:72380] " email
2015-12-19 14:37 ` [ruby-core:72382] " headius
2015-12-19 15:01 ` [ruby-core:72383] " email
2015-12-19 15:27 ` [ruby-core:72386] " headius
2015-12-19 15:29 ` [ruby-core:72387] " headius
2015-12-20  8:54 ` [ruby-core:72408] " funny.falcon
2016-01-25 18:21   ` [ruby-core:73429] " Andrew Vit
2017-01-31  7:12 ` [ruby-core:79334] [Ruby trunk Bug#11822][Closed] " ko1

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).