philiprehberger-task_queue 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2524cd95e75d5fef01076f0bb4895a3d3aceeed233a2b68eabc596c7a731ebfa
4
- data.tar.gz: 7df5392a5796de69bf086cc47484cb5481f1e131099d3994062e5e3c308d4b06
3
+ metadata.gz: 6a1d028dda2118c9948cbec0cdff5f7215eeb8d76615acdeaf9a2e7b5f6c45ce
4
+ data.tar.gz: 94d56b9b9acb20add7a8e91dd1977f1a87a135e442dadc4c0b8acfc4eb5b5ac1
5
5
  SHA512:
6
- metadata.gz: 60adcdcd0b47caba95f1eb03c8be5e340e62aaf9fe2d9ed5ac85a702e1ac89d95e65c810a245bd5d9d0ddbd7fcbc68c493c46497a53298fe7721f90240107582
7
- data.tar.gz: 1657f7b9c9b358a58ca2aae35896374e5193a295e753d47054f662233237fef68148175d8e7e40a050bf102fb68f32cf790827f79f84b9450fea7d0be75ab8ac
6
+ metadata.gz: 4c6f850324f487d516626884c44278bce4927b2419ce9b0036dd2d1c399b41a006c2ab0d48a3f18ef60a9ebcf00ef82fbf6a70e3bc486b78cdea4c397e9dc246
7
+ data.tar.gz: efb376ce60b1b9bfe43676ccefb5c0a2d03874e61d897337a6c0268555477478528583be36ab4763c5c0422b1e0fd4734db6bfa7dd738757a9e2f7616b8415f9
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-04-05
11
+
12
+ ### Added
13
+ - `in_flight` count in `stats` hash for monitoring active task execution
14
+ - `Queue#pause` and `Queue#resume` for temporarily suspending task consumption
15
+ - `Queue#paused?` to check pause state
16
+ - `Queue#clear` to discard all pending tasks
17
+
10
18
  ## [0.3.0] - 2026-04-04
11
19
 
12
20
  ### Added
data/README.md CHANGED
@@ -64,7 +64,7 @@ queue.push { File.read("/nonexistent") }
64
64
 
65
65
  queue.drain(timeout: 5)
66
66
  puts queue.stats
67
- # => { completed: 0, failed: 2, pending: 0 }
67
+ # => { completed: 0, failed: 2, pending: 0, in_flight: 0 }
68
68
  ```
69
69
 
70
70
  ### Completion callback
@@ -88,7 +88,7 @@ queue.drain(timeout: 5)
88
88
 
89
89
  ### Statistics
90
90
 
91
- `stats` returns a snapshot of completed, failed, and pending counts. All counters are thread-safe and updated atomically after each task finishes.
91
+ `stats` returns a snapshot of completed, failed, pending, and in-flight counts. All counters are thread-safe and updated atomically after each task finishes.
92
92
 
93
93
  ```ruby
94
94
  queue = Philiprehberger::TaskQueue.new(concurrency: 4)
@@ -100,9 +100,44 @@ stats = queue.stats
100
100
  puts "Completed: #{stats[:completed]}"
101
101
  puts "Failed: #{stats[:failed]}"
102
102
  puts "Pending: #{stats[:pending]}"
103
+ puts "In-flight: #{stats[:in_flight]}"
103
104
  # Completed: 19
104
105
  # Failed: 1
105
106
  # Pending: 0
107
+ # In-flight: 0
108
+ ```
109
+
110
+ ### Pause and resume
111
+
112
+ Temporarily suspend task consumption without shutting down. In-flight tasks will finish, but no new tasks are picked up until the queue is resumed.
113
+
114
+ ```ruby
115
+ queue = Philiprehberger::TaskQueue.new(concurrency: 4)
116
+
117
+ 10.times { |i| queue.push { process(i) } }
118
+
119
+ queue.pause
120
+ puts queue.paused? # => true
121
+
122
+ # Tasks already in flight will complete, but pending tasks wait.
123
+ queue.resume
124
+ puts queue.paused? # => false
125
+
126
+ queue.shutdown(timeout: 10)
127
+ ```
128
+
129
+ ### Clear pending tasks
130
+
131
+ Discard all pending tasks from the queue. Returns the number of tasks removed.
132
+
133
+ ```ruby
134
+ queue = Philiprehberger::TaskQueue.new(concurrency: 2)
135
+
136
+ 100.times { |i| queue.push { process(i) } }
137
+ cleared = queue.clear
138
+ puts "Cleared #{cleared} tasks"
139
+
140
+ queue.shutdown(timeout: 5)
106
141
  ```
107
142
 
108
143
  ### FIFO ordering guarantees
@@ -161,8 +196,12 @@ queue.shutdown(timeout: 5)
161
196
  | `#shutdown(timeout:)` | `timeout` — seconds to wait for workers (Numeric, default `30`) | `nil` | Signal workers to stop, drain remaining tasks, join threads up to `timeout` seconds |
162
197
  | `#on_complete(&block)` | `&block` — callback receiving `(result)` | `self` | Register a callback invoked after each successful task completion with the task's return value |
163
198
  | `#on_error(&block)` | `&block` — callback receiving `(exception, task)` | `self` | Register an error callback invoked when a task raises a `StandardError` |
164
- | `#stats` | _(none)_ | `Hash` | Returns `{ completed:, failed:, pending: }` with Integer counts |
199
+ | `#stats` | _(none)_ | `Hash` | Returns `{ completed:, failed:, pending:, in_flight: }` with Integer counts |
165
200
  | `#drain(timeout:)` | `timeout` — seconds to wait (Numeric, default `30`) | `nil` | Block until all pending and in-flight tasks complete without shutting down |
201
+ | `#pause` | _(none)_ | `self` | Suspend task consumption; in-flight tasks finish but no new tasks are picked up |
202
+ | `#resume` | _(none)_ | `self` | Resume a paused queue, waking workers to continue processing |
203
+ | `#paused?` | _(none)_ | `Boolean` | Whether the queue is currently paused |
204
+ | `#clear` | _(none)_ | `Integer` | Remove all pending tasks and return the number cleared |
166
205
 
167
206
  ## Development
168
207
 
@@ -19,6 +19,8 @@ module Philiprehberger
19
19
  @workers = []
20
20
  @running = true
21
21
  @started = false
22
+ @paused = false
23
+ @pause_condition = ConditionVariable.new
22
24
  @error_handler = nil
23
25
  @complete_handler = nil
24
26
  @stats = { completed: 0, failed: 0, in_flight: 0 }
@@ -48,10 +50,53 @@ module Philiprehberger
48
50
 
49
51
  # Return statistics about processed tasks.
50
52
  #
51
- # @return [Hash{Symbol => Integer}] counts for :completed, :failed, :pending
53
+ # @return [Hash{Symbol => Integer}] counts for :completed, :failed, :pending, :in_flight
52
54
  def stats
53
55
  @mutex.synchronize do
54
- { completed: @stats[:completed], failed: @stats[:failed], pending: @tasks.size }
56
+ { completed: @stats[:completed], failed: @stats[:failed], pending: @tasks.size,
57
+ in_flight: @stats[:in_flight] }
58
+ end
59
+ end
60
+
61
+ # Pause the queue so workers stop dequeuing new tasks.
62
+ #
63
+ # In-flight tasks will finish, but no new tasks will be picked up until
64
+ # +resume+ is called.
65
+ #
66
+ # @return [self]
67
+ def pause
68
+ @mutex.synchronize do
69
+ @paused = true
70
+ end
71
+ self
72
+ end
73
+
74
+ # Resume a paused queue, waking workers to continue processing.
75
+ #
76
+ # @return [self]
77
+ def resume
78
+ @mutex.synchronize do
79
+ @paused = false
80
+ @pause_condition.broadcast
81
+ end
82
+ self
83
+ end
84
+
85
+ # Whether the queue is currently paused.
86
+ #
87
+ # @return [Boolean]
88
+ def paused?
89
+ @mutex.synchronize { @paused }
90
+ end
91
+
92
+ # Remove all pending tasks from the queue.
93
+ #
94
+ # @return [Integer] number of tasks cleared
95
+ def clear
96
+ @mutex.synchronize do
97
+ count = @tasks.size
98
+ @tasks.clear
99
+ count
55
100
  end
56
101
  end
57
102
 
@@ -128,8 +173,10 @@ module Philiprehberger
128
173
  return unless @running
129
174
 
130
175
  @running = false
176
+ @paused = false
131
177
  @workers.each(&:stop)
132
178
  @condition.broadcast
179
+ @pause_condition.broadcast
133
180
  end
134
181
  end
135
182
 
@@ -146,7 +193,8 @@ module Philiprehberger
146
193
  @workers << Worker.new(
147
194
  @tasks, @mutex, @condition,
148
195
  context: { stats: @stats, error_handler: @error_handler,
149
- complete_handler: @complete_handler, drain_condition: @drain_condition }
196
+ complete_handler: @complete_handler, drain_condition: @drain_condition,
197
+ paused: -> { @paused }, pause_condition: @pause_condition }
150
198
  )
151
199
  end
152
200
  @started = true
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module TaskQueue
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
@@ -14,6 +14,8 @@ module Philiprehberger
14
14
  @error_handler = context[:error_handler]
15
15
  @complete_handler = context[:complete_handler]
16
16
  @drain_condition = context[:drain_condition]
17
+ @paused = context[:paused]
18
+ @pause_condition = context[:pause_condition]
17
19
  @running = true
18
20
  @thread = Thread.new { run }
19
21
  end
@@ -43,6 +45,9 @@ module Philiprehberger
43
45
  @condition.wait(@mutex) while @queue.empty? && @running
44
46
  return nil unless @running || !@queue.empty?
45
47
 
48
+ @pause_condition.wait(@mutex) while @paused&.call && @running
49
+ return nil unless @running || !@queue.empty?
50
+
46
51
  @stats[:in_flight] += 1
47
52
  @queue.shift
48
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-task_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-05 00:00:00.000000000 Z
11
+ date: 2026-04-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A lightweight, zero-dependency, thread-safe in-process async job queue
14
14
  with configurable concurrency for Ruby applications.