Class: Philiprehberger::QueueStack::Stack
- Inherits:
-
Object
- Object
- Philiprehberger::QueueStack::Stack
- Includes:
- Enumerable
- Defined in:
- lib/philiprehberger/queue_stack/stack.rb
Overview
Thread-safe LIFO stack with optional capacity limit and blocking operations.
Instance Method Summary collapse
-
#clear ⇒ void
Remove all items without returning them.
-
#close ⇒ void
Mark the stack as closed.
-
#closed? ⇒ Boolean
Whether the stack has been closed.
-
#drain ⇒ Array
Remove and return all items as an array (LIFO order, top first).
-
#each {|item| ... } ⇒ Enumerator, self
Iterate items without removing them (snapshot of current state, LIFO order).
-
#empty? ⇒ Boolean
Whether the stack is empty.
-
#full? ⇒ Boolean
Whether the stack is at capacity.
-
#initialize(capacity: nil) ⇒ Stack
constructor
Create a new stack.
-
#peek ⇒ Object?
Peek at the top item without removing it.
-
#pop ⇒ Object?
Pop and return the top item.
-
#pop_if {|item| ... } ⇒ Object?
Conditionally pop the top item.
-
#push(item) ⇒ void
Push an item onto the top of the stack.
-
#size ⇒ Integer
Return the number of items in the stack.
-
#to_a ⇒ Array
Return a snapshot of items as an array (LIFO order, top first).
-
#try_pop(timeout:) ⇒ Object?
Try to pop an item with a timeout.
-
#try_push(item, timeout: nil) ⇒ Boolean
Try to push an item without blocking indefinitely.
Constructor Details
#initialize(capacity: nil) ⇒ Stack
Create a new stack.
17 18 19 20 21 22 23 24 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 17 def initialize(capacity: nil) @items = [] @capacity = capacity @closed = false @mutex = Mutex.new @not_empty = ConditionVariable.new @not_full = ConditionVariable.new end |
Instance Method Details
#clear ⇒ void
This method returns an undefined value.
Remove all items without returning them. Signals any blocked producers.
92 93 94 95 96 97 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 92 def clear @mutex.synchronize do @items.clear @not_full.broadcast end end |
#close ⇒ void
This method returns an undefined value.
Mark the stack as closed. New push calls will raise ClosedError. Existing items can still be popped. Wakes all waiting threads.
174 175 176 177 178 179 180 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 174 def close @mutex.synchronize do @closed = true @not_empty.broadcast @not_full.broadcast end end |
#closed? ⇒ Boolean
Whether the stack has been closed.
185 186 187 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 185 def closed? @mutex.synchronize { @closed } end |
#drain ⇒ Array
Remove and return all items as an array (LIFO order, top first). Non-blocking.
141 142 143 144 145 146 147 148 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 141 def drain @mutex.synchronize do result = @items.reverse @items.clear @not_full.broadcast result end end |
#each {|item| ... } ⇒ Enumerator, self
Iterate items without removing them (snapshot of current state, LIFO order). Returns an Enumerator if no block is given.
155 156 157 158 159 160 161 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 155 def each(&block) snapshot = @mutex.synchronize { @items.reverse } return snapshot.each unless block snapshot.each(&block) self end |
#empty? ⇒ Boolean
Whether the stack is empty.
206 207 208 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 206 def empty? @mutex.synchronize { @items.empty? } end |
#full? ⇒ Boolean
Whether the stack is at capacity.
213 214 215 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 213 def full? @mutex.synchronize { @capacity ? @items.length >= @capacity : false } end |
#peek ⇒ Object?
Peek at the top item without removing it.
192 193 194 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 192 def peek @mutex.synchronize { @items.last } end |
#pop ⇒ Object?
Pop and return the top item. Blocks if empty (returns nil if closed and empty).
44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 44 def pop @mutex.synchronize do while @items.empty? return nil if @closed @not_empty.wait(@mutex) end item = @items.pop @not_full.signal item end end |
#pop_if {|item| ... } ⇒ Object?
Conditionally pop the top item. The block is called with the item that would be popped next. If the block returns truthy, the item is removed and returned. Otherwise the item is left in place and nil is returned. Returns nil immediately if the stack is empty (non-blocking).
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 106 def pop_if @mutex.synchronize do return nil if @items.empty? return nil unless yield(@items.last) item = @items.pop @not_full.signal item end end |
#push(item) ⇒ void
This method returns an undefined value.
Push an item onto the top of the stack. Blocks if at capacity.
31 32 33 34 35 36 37 38 39 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 31 def push(item) @mutex.synchronize do raise ClosedError, 'cannot push on a closed stack' if @closed @not_full.wait(@mutex) while @capacity && @items.length >= @capacity @items.push(item) @not_empty.signal end end |
#size ⇒ Integer
Return the number of items in the stack.
199 200 201 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 199 def size @mutex.synchronize { @items.length } end |
#to_a ⇒ Array
Return a snapshot of items as an array (LIFO order, top first).
166 167 168 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 166 def to_a @mutex.synchronize { @items.reverse } end |
#try_pop(timeout:) ⇒ Object?
Try to pop an item with a timeout.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 121 def try_pop(timeout:) deadline = Time.now + timeout @mutex.synchronize do while @items.empty? return nil if @closed remaining = deadline - Time.now return nil if remaining <= 0 @not_empty.wait(@mutex, remaining) end item = @items.pop @not_full.signal item end end |
#try_push(item, timeout: nil) ⇒ Boolean
Try to push an item without blocking indefinitely.
With timeout: nil, returns immediately. With a numeric timeout, waits up to that many seconds for space to become available.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/philiprehberger/queue_stack/stack.rb', line 66 def try_push(item, timeout: nil) @mutex.synchronize do raise ClosedError, 'cannot push on a closed stack' if @closed if @capacity && @items.length >= @capacity return false if timeout.nil? || timeout <= 0 deadline = Time.now + timeout while @items.length >= @capacity remaining = deadline - Time.now return false if remaining <= 0 @not_full.wait(@mutex, remaining) raise ClosedError, 'cannot push on a closed stack' if @closed end end @items.push(item) @not_empty.signal true end end |