Issue #10600 has been updated by John Anderson.

File queue-close-2.diff added

I thought this specification would be more clear:

~~~
/*
 * Document-method: Queue#close
 * call-seq: close
 *
 * Closes the queue to producers. A closed queue cannot be re-opened.
 *
 * After the call to close completes, the following are true:
 *
 * - closed? will return true
 *
 * - calling enq/push/<< will raise an exception
 *
 * - calling deq/pop/shift will return an object from the queue as usual.
 *
 * - when empty? is true, deq(non_block=false) will not suspend and
 *   will return nil. deq(non_block=true) will raise an exception.
 *
 * And for SizedQueue, these will also be true:
 *
 * - each thread already suspended in enq at the time of the call
 *   to close will be allowed to push its object as usual.
 *
 * - empty? will be false when there are either objects in the queue, or
 *   producers which were suspended at the time of the call to close but whose
 *   objects are not yet in the queue. Therefore, it can be true (very
 *   briefly) that empty? == false && size == 0, since size returns the number
 *   of objects actually in the queue.
 */
~~~

An updated patch to implement that is attached. I've written some updated tests as well, but I've left those out of the patch for now.

I thought about (4) some more. What I've implemented in this patch was more difficult than throwing an exception as I suggested previously, but I think the semantics of this approach are somewhat less surprising.


----------------------------------------
Feature #10600: [PATCH] Queue#close
https://bugs.ruby-lang.org/issues/10600#change-50462

* Author: John Anderson
* Status: Open
* Priority: Normal
* Assignee: 
* Category: 
* Target version: 
----------------------------------------
In a multiple-producer / multiple-consumer situation using blocking enq and deq, closing a queue cleanly is difficult. It's possible using a queue poison token, but unpleasant because either producers have to know how to match up number of poison tokens with number of consumers, or consumers have to keep putting the poison back into the queue which complicates testing for empty and not blocking on deq.

This patch (from trunk at b2a128f) implements Queue#close which will close the queue to producers, leaving consumers to deq the remaining items. Once the queue is both closed and empty, consumers will not block. When an empty queue is closed, all consumers blocking on deq will be woken up and given nil.

With Queue#close, clean queue shutdown is simple:

~~~ ruby
queue = SizedQueue.new 1000

consumer_threads = lots_of.times.map do
  Thread.new do
    while item = queue.pop
      do_work item
    end
  end
end

source = somewhat_async_enumerator

producer_threads = a_few.times.map do
  Thread.new do
    loop{queue << source.next}
  end
end

producer_threads.each &:join
queue.close
consumer_threads.each &:join
~~~


---Files--------------------------------
queue-close.diff (5.18 KB)
queue-close-2.diff (10.2 KB)


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