class Async::PriorityQueue
@public Since *Async v2*.
consumers are served first when items become available.
assigns priorities to consumers (fibers waiting to dequeue). Higher priority
Unlike a traditional priority queue where items have priorities, this queue
A queue which allows items to be processed in priority order of consumers.
def <<(item)
def <<(item) self.push(item) end
def async(priority: 0, parent: (@parent or Task.current), **options, &block)
@parameter options [Hash] The options to pass to the task.
@parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
@parameter priority [Numeric] The priority for processing items.
@asynchronous Executes the given block concurrently for each item.
Process each item in the queue.
def async(priority: 0, parent: (@parent or Task.current), **options, &block) while item = self.dequeue(priority: priority) parent.async(item, **options, &block) end end
def close
Close the queue, causing all waiting tasks to return `nil`.
def close @mutex.synchronize do @closed = true # Signal all waiting fibers with nil, skipping dead/invalid ones: while waiter = @waiting.pop waiter.signal(nil) end end end
def closed?
def closed? @closed end
def dequeue(priority: 0, timeout: nil)
@parameter timeout [Numeric, nil] Maximum time to wait for an item. If nil, waits indefinitely. If 0, returns immediately.
@parameter priority [Numeric] The priority of this consumer (higher = served first).
items first.
Fibers are served in priority order, with higher priority fibers receiving
If the queue is empty, this method will block until an item is available or timeout expires.
Remove and return the next item from the queue.
def dequeue(priority: 0, timeout: nil) @mutex.synchronize do # If queue is closed and empty, return nil immediately: if @closed && @items.empty? return nil end # Fast path: if items available and either no waiters or we have higher priority: unless @items.empty? head = @waiting.peek if head.nil? or priority > head.priority return @items.shift end end # Handle immediate timeout (non-blocking) return nil if timeout == 0 # Need to wait - create our own condition variable and add to waiting queue: sequence = @sequence @sequence += 1 condition = ConditionVariable.new begin waiter = Waiter.new(Fiber.current, priority, sequence, condition, nil) @waiting.push(waiter) # Wait for our specific condition variable to be signaled: return waiter.wait_for_value(@mutex, timeout) ensure waiter&.invalidate! end end end
def each(priority: 0)
Enumerate each item in the queue.
def each(priority: 0) while item = self.dequeue(priority: priority) yield item end end
def empty?
def empty? @items.empty? end
def enqueue(*items)
Add multiple items to the queue.
def enqueue(*items) @mutex.synchronize do if @closed raise ClosedError, "Cannot enqueue items to a closed queue." end @items.concat(items) # Wake up waiting fibers in priority order, skipping dead/invalid waiters: while !@items.empty? && (waiter = @waiting.pop) if waiter.valid? value = @items.shift waiter.signal(value) end # Dead/invalid waiter discarded, continue to next one. end end end
def initialize(parent: nil)
Create a new priority queue.
def initialize(parent: nil) @items = [] @closed = false @parent = parent @waiting = IO::Event::PriorityHeap.new @sequence = 0 @mutex = Mutex.new end
def pop(priority: 0, timeout: nil)
@parameter timeout [Numeric, nil] Maximum time to wait for an item. If nil, waits indefinitely. If 0, returns immediately.
@parameter priority [Numeric] The priority of this consumer.
Compatibility with {::Queue#pop}.
def pop(priority: 0, timeout: nil) self.dequeue(priority: priority, timeout: timeout) end
def push(item)
Add an item to the queue.
def push(item) @mutex.synchronize do if @closed raise ClosedError, "Cannot push items to a closed queue." end @items << item # Wake up the highest priority waiter if any, skipping dead/invalid waiters: while waiter = @waiting.pop if waiter.valid? value = @items.shift waiter.signal(value) break end # Dead/invalid waiter discarded, try next one. end end end
def signal(value = nil)
def signal(value = nil) self.enqueue(value) end
def size
def size @items.size end
def wait(priority: 0)
Wait for an item to be available, the same as {#dequeue}.
def wait(priority: 0) self.dequeue(priority: priority) end
def waiting_count
def waiting_count @mutex.synchronize do @waiting.size end end