[ruby-core:109596] [Ruby master Feature#18965] Further Thread::Queue improvements
From:
"byroot (Jean Boussier)" <noreply@...>
Date:
2022-08-20 12:48:39 UTC
List:
ruby-core #109596
Issue #18965 has been updated by byroot (Jean Boussier).
`pop(nonblock: true)` that wouldn't raise definitely make sense to me, so I'll for sure propose it at the next meeting.
> Also depending on the Queue/SizedQueue implementation (typical for non-blocking queues) there might simply not be a way to do batch push/pop,
Is it true for both push and pop? I can see the issue for push, but no so much for pop.
I no longer think a batch push is necessary, so I'll remove that part of the proposal, but I still think `pop(count: )` makes the calling code much much simpler, so I'd still like to have it even if it doesn't improve performance significantly.
----------------------------------------
Feature #18965: Further Thread::Queue improvements
https://bugs.ruby-lang.org/issues/18965#change-98778
* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
Following the recent addition of a `timeout` parameter to `Queue#pop`, there are a handful of other improvements I'd like to make.
### Batch insert
When using the queue for batch processing, it would be good to be able to push multiple elements at once:
Currently you have to call `push` repeatedly
```ruby
items.each do |item|
queue.push(item)
end
```
That's wasteful because on each call we check wether the queue is closed, try to wakeup blocked threads, etc.
It would be much better if you could do:
```ruby
queue.concat(items)
```
With of course both `nonblock` and `timeout` support.
Then there's the question of how `SizedQueue` would behave if it's not full, but still doesn't have space for all the elements. e.g.
```ruby
queue = SizedQueue.new(10)
queue.concat(6.times.to_a)
queue.concat(6.times.to_a) # Block until there is 6 free slots?
```
I think the simplest would be to wait for enough space to append the entire set, because combined with a timeout, it would be awkward if only part of the array was concatenated.
### Batch pop
Similarly, sometimes the consumer of a queue is capable of batching, and right now it's not efficient:
```ruby
loop do
items = [queue.pop]
begin
99.times do
items << queue.pop(true) # true is for nonblock
end
rescue ThreadError # empty queue
end
process_items(items)
end
```
It would be much more efficient if `pop` accepted a `count` parameter:
```ruby
loop do
items = queue.pop(count: 100)
process_items(items)
end
```
The behavior would be:
- Block if the queue is empty
- If it's not empty, return **up to** `count` items (Just like `Array#pop`)
### Non blocking mode, without exception
As shown above, the current `nonblock` parameter is a bit awkward, because:
- It raises an exception, which is very expensive for a construct often used in "low level" code.
- The exception is `ThreadError`, so you may have to match the error message for `"queue empty"`, to make sure it doesn't come from a Mutex issue or something like that.
I believe that we could introduce a keyword argument:
```ruby
Queue.new.pop(nonblock: true) # => nil
```
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>