From: ko1@... Date: 2015-08-26T23:19:51+00:00 Subject: [ruby-core:70598] [Ruby trunk - Feature #10600] [Assigned] [PATCH] Queue#close Issue #10600 has been updated by Koichi Sasada. Status changed from Closed to Assigned I committed r51699 to try Queue#close. I changed proposed behavior: * #close(token=nil) -> #close(exception=false) (variant of (5) in #10) because: * I feel strange that raising exception if token is Exception (I can't pass Exception objects with token) * Considering exception name is not valuable task. Only "ClosedQueueError" is enough. No need to worry about exception type. * wake-up all blocking threads waiting enq for SizedQueue and raise ClosedQueueError because: * waiting threads can block eternally if no consumer threads deq a Queue. * It is simple rule to know: "nobody can not enq closed Queue". I think "waiting for enq" is BEFORE enq. Could you try that? Discussion: * How about the above (committed) specification? * ClosedQueueError inherits StopIteration, not ThreadError. Is it okay? * "exception" optional parameter is reasonable or not? Should be "#close(exception: false)" or "#close!"? BTW, I found that it is nice feature to synchronize starting multiple threads together. ```ruby synq = Queue.new 10.times{ Thread.new{ synq.pop #=> nil from closed Queue. # do something } } # do something initialization synq.close ``` ---------------------------------------- Feature #10600: [PATCH] Queue#close https://bugs.ruby-lang.org/issues/10600#change-54007 * Author: John Anderson * Status: Assigned * Priority: Normal * Assignee: Koichi Sasada ---------------------------------------- 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) patch-25f99aef.diff (25.2 KB) queue_benchmark.rb (2.95 KB) -- https://bugs.ruby-lang.org/