10
10
require_relative "notification"
11
11
12
12
module Async
13
- # A queue which allows items to be processed in order.
13
+ # A thread-safe queue which allows items to be processed in order.
14
+ #
15
+ # This implementation uses Thread::Queue internally for thread safety while
16
+ # maintaining compatibility with the fiber scheduler.
14
17
#
15
18
# It has a compatible interface with {Notification} and {Condition}, except that it's multi-value.
16
19
#
@@ -21,15 +24,14 @@ class Queue
21
24
class ClosedError < RuntimeError
22
25
end
23
26
24
- # Create a new queue.
27
+ # Create a new thread-safe queue.
25
28
#
26
29
# @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
27
- # @parameter available [Notification] The notification to use for signaling when items are available.
28
- def initialize ( parent : nil , available : Notification . new )
29
- @items = [ ]
30
- @closed = false
30
+ # @parameter available [Notification] The notification to use for signaling when items are available. (ignored, for compatibility)
31
+ def initialize ( parent : nil , available : nil )
32
+ @queue = Thread ::Queue . new
31
33
@parent = parent
32
- @available = available
34
+ @closed = false
33
35
end
34
36
35
37
# @returns [Boolean] Whether the queue is closed.
@@ -40,23 +42,23 @@ def closed?
40
42
# Close the queue, causing all waiting tasks to return `nil`. Any subsequent calls to {enqueue} will raise an exception.
41
43
def close
42
44
@closed = true
43
-
44
- while @available . waiting?
45
- @available . signal ( nil )
46
- end
45
+ @queue . close
47
46
end
48
47
49
48
# @attribute [Array] The items in the queue.
50
- attr :items
49
+ # @deprecated Use {#size} instead. This method is provided for compatibility but returns nil.
50
+ def items
51
+ nil # Thread::Queue doesn't expose internal items
52
+ end
51
53
52
54
# @returns [Integer] The number of items in the queue.
53
55
def size
54
- @items . size
56
+ @queue . size
55
57
end
56
58
57
59
# @returns [Boolean] Whether the queue is empty.
58
60
def empty?
59
- @items . empty?
61
+ @queue . empty?
60
62
end
61
63
62
64
# Add an item to the queue.
@@ -65,9 +67,7 @@ def push(item)
65
67
raise ClosedError , "Cannot push items to a closed queue."
66
68
end
67
69
68
- @items << item
69
-
70
- @available . signal unless self . empty?
70
+ @queue . push ( item )
71
71
end
72
72
73
73
# Compatibility with {::Queue#push}.
@@ -81,22 +81,26 @@ def enqueue(*items)
81
81
raise ClosedError , "Cannot enqueue items to a closed queue."
82
82
end
83
83
84
- @items . concat ( items )
85
-
86
- @available . signal unless self . empty?
84
+ items . each { |item | @queue . push ( item ) }
87
85
end
88
86
89
87
# Remove and return the next item from the queue.
90
88
def dequeue
91
- while @items . empty?
92
- if @closed
93
- return nil
94
- end
89
+ return nil if @closed && @queue . empty?
90
+
91
+ begin
92
+ # Try non-blocking first
93
+ @queue . pop ( true )
94
+ rescue ThreadError
95
+ # Queue is empty, check if closed
96
+ return nil if @closed
95
97
96
- @available . wait
98
+ # Use blocking pop - the fiber scheduler will handle this properly
99
+ # in Ruby's fiber scheduler implementation
100
+ @queue . pop ( false )
97
101
end
98
-
99
- @items . shift
102
+ rescue ClosedQueueError
103
+ nil
100
104
end
101
105
102
106
# Compatibility with {::Queue#pop}.
@@ -136,7 +140,7 @@ def wait
136
140
end
137
141
end
138
142
139
- # A queue which limits the number of items that can be enqueued.
143
+ # A thread-safe queue which limits the number of items that can be enqueued.
140
144
# @public Since *Async v1*.
141
145
class LimitedQueue < Queue
142
146
# @private This exists purely for emitting a warning.
@@ -149,30 +153,20 @@ def self.new(...)
149
153
# Create a new limited queue.
150
154
#
151
155
# @parameter limit [Integer] The maximum number of items that can be enqueued.
152
- # @parameter full [Notification] The notification to use for signaling when the queue is full.
153
- def initialize ( limit = 1 , full : Notification . new , **options )
156
+ # @parameter full [Notification] The notification to use for signaling when the queue is full. (ignored, for compatibility)
157
+ def initialize ( limit = 1 , full : nil , **options )
154
158
super ( **options )
155
-
156
- @limit = limit
157
- @full = full
159
+ @queue = Thread ::SizedQueue . new ( limit )
158
160
end
159
161
160
162
# @attribute [Integer] The maximum number of items that can be enqueued.
161
- attr :limit
162
-
163
- # Close the queue, causing all waiting tasks to return `nil`. Any subsequent calls to {enqueue} will raise an exception.
164
- # Also signals all tasks waiting for the queue to be full.
165
- def close
166
- super
167
-
168
- while @full . waiting?
169
- @full . signal ( nil )
170
- end
163
+ def limit
164
+ @queue . max
171
165
end
172
166
173
167
# @returns [Boolean] Whether trying to enqueue an item would block.
174
168
def limited?
175
- !@closed && @items . size >= @limit
169
+ !@closed && @queue . size >= @queue . max
176
170
end
177
171
178
172
# Add an item to the queue.
@@ -181,46 +175,24 @@ def limited?
181
175
#
182
176
# @parameter item [Object] The item to add to the queue.
183
177
def push ( item )
184
- while limited?
185
- @full . wait
178
+ if @closed
179
+ raise ClosedError , "Cannot push items to a closed queue."
186
180
end
187
181
188
- super
182
+ begin
183
+ @queue . push ( item ) # This will block if queue is full
184
+ rescue ClosedQueueError
185
+ raise ClosedError , "Cannot push items to a closed queue."
186
+ end
189
187
end
190
188
191
189
# Add multiple items to the queue.
192
190
#
193
- # If the queue is full, this method will block until there is space available.
191
+ # If the queue is full, this method will block until there is space available.
194
192
#
195
193
# @parameter items [Array] The items to add to the queue.
196
194
def enqueue ( *items )
197
- while !items . empty?
198
- while limited?
199
- @full . wait
200
- end
201
-
202
- if @closed
203
- raise ClosedError , "Cannot enqueue items to a closed queue."
204
- end
205
-
206
- available = @limit - @items . size
207
- @items . concat ( items . shift ( available ) )
208
-
209
- @available . signal unless self . empty?
210
- end
211
- end
212
-
213
- # Remove and return the next item from the queue.
214
- #
215
- # If the queue is empty, this method will block until an item is available.
216
- #
217
- # @returns [Object] The next item in the queue.
218
- def dequeue
219
- item = super
220
-
221
- @full . signal
222
-
223
- return item
195
+ items . each { |item | push ( item ) }
224
196
end
225
197
end
226
198
end
0 commit comments