pigeon 0.4.2 → 0.4.3
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.
- data/VERSION +1 -1
- data/lib/pigeon/engine.rb +25 -0
- data/lib/pigeon/processor.rb +18 -7
- data/lib/pigeon/queue.rb +51 -2
- data/lib/pigeon/scheduler.rb +6 -0
- data/pigeon.gemspec +2 -2
- data/test/unit/pigeon_launcher_test.rb +6 -4
- data/test/unit/pigeon_processor_test.rb +35 -0
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.3
|
data/lib/pigeon/engine.rb
CHANGED
@@ -212,6 +212,9 @@ class Pigeon::Engine
|
|
212
212
|
@task_lock = Mutex.new
|
213
213
|
@task_locks = { }
|
214
214
|
|
215
|
+
@task_register_lock = Mutex.new
|
216
|
+
@registered_tasks = { }
|
217
|
+
|
215
218
|
self.logger ||= self.engine_logger
|
216
219
|
self.logger.level = Pigeon::Logger::DEBUG if (self.debug?)
|
217
220
|
|
@@ -344,6 +347,28 @@ class Pigeon::Engine
|
|
344
347
|
def foreground?
|
345
348
|
!!self.foreground
|
346
349
|
end
|
350
|
+
|
351
|
+
# Registers a task with the engine. The given task will then be included
|
352
|
+
# in the list returned by registered_tasks.
|
353
|
+
def register_task(task)
|
354
|
+
@task_register_lock.synchronize do
|
355
|
+
@registered_tasks[task] = task
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
# Removes a task from the list of tasks registered with this engine.
|
360
|
+
def unregister_task(task)
|
361
|
+
@task_register_lock.synchronize do
|
362
|
+
@registered_tasks.delete(task)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Returns a list of tasks that have been registered with the engine.
|
367
|
+
def registered_tasks
|
368
|
+
@task_register_lock.synchronize do
|
369
|
+
@registered_tasks.values
|
370
|
+
end
|
371
|
+
end
|
347
372
|
|
348
373
|
protected
|
349
374
|
def run_chain(chain_name)
|
data/lib/pigeon/processor.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
class Pigeon::Processor
|
2
2
|
# == Exceptions ===========================================================
|
3
3
|
|
4
|
-
class AlreadyBoundToQueue < Exception
|
5
|
-
end
|
6
|
-
|
7
4
|
# == Constants ============================================================
|
8
5
|
|
9
6
|
# == Properties ===========================================================
|
@@ -15,22 +12,31 @@ class Pigeon::Processor
|
|
15
12
|
|
16
13
|
# == Instance Methods =====================================================
|
17
14
|
|
15
|
+
# Creates a new processor. An optional queue can be specified in which case
|
16
|
+
# the processor will register itself as an observer of that queue. A block
|
17
|
+
# can be given to filter the tasks contained in the associated queue.
|
18
18
|
def initialize(queue = nil, &filter)
|
19
19
|
@id = Pigeon::Support.unique_id
|
20
20
|
@lock = Mutex.new
|
21
21
|
@filter = filter || lambda { |task| true }
|
22
22
|
|
23
|
-
|
23
|
+
if (queue)
|
24
|
+
self.queue = queue
|
24
25
|
|
25
|
-
|
26
|
+
switch_to_next_task!
|
27
|
+
end
|
26
28
|
end
|
27
29
|
|
30
|
+
# Assigns this processor to a particular queue. If one is already assigned
|
31
|
+
# then the observer callback for that queue will be removed.
|
28
32
|
def queue=(queue)
|
29
|
-
|
33
|
+
if (@queue)
|
34
|
+
@queue.remove_observer(&@claim)
|
35
|
+
end
|
30
36
|
|
31
37
|
@queue = queue
|
32
38
|
|
33
|
-
@
|
39
|
+
@claim = lambda do |task|
|
34
40
|
@lock.synchronize do
|
35
41
|
if (!@task and @filter.call(task))
|
36
42
|
@task = queue.claim(task)
|
@@ -41,12 +47,17 @@ class Pigeon::Processor
|
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
50
|
+
|
51
|
+
@queue.observe(&@claim)
|
44
52
|
end
|
45
53
|
|
54
|
+
# Returns true if the given task would be accepted by the filter defined
|
55
|
+
# for this processor.
|
46
56
|
def accept?(task)
|
47
57
|
@filter.call(task)
|
48
58
|
end
|
49
59
|
|
60
|
+
# Returns true if a task is currently being processed, false otherwise.
|
50
61
|
def task?
|
51
62
|
!!@task
|
52
63
|
end
|
data/lib/pigeon/queue.rb
CHANGED
@@ -29,18 +29,26 @@ class Pigeon::Queue
|
|
29
29
|
|
30
30
|
# == Class Methods ========================================================
|
31
31
|
|
32
|
+
# Returns the current filter configuration. This is stored as a Hash with
|
33
|
+
# the key being the filter name, the value being the matching block. The
|
34
|
+
# nil key is the default filter which accepts all tasks.
|
32
35
|
def self.filters
|
33
36
|
@filters ||= {
|
34
37
|
nil => lambda { |task| true }
|
35
38
|
}
|
36
39
|
end
|
37
40
|
|
41
|
+
# Defines a new filter with the given name and uses the supplied block to
|
42
|
+
# evaluate if a task qualifies or not.
|
38
43
|
def self.filter(name, &block)
|
39
44
|
filters[name] = block
|
40
45
|
end
|
41
46
|
|
42
47
|
# == Instance Methods =====================================================
|
43
48
|
|
49
|
+
# Creates a new queue. If a block is given, it is used to compare two tasks
|
50
|
+
# and order them, so it should take two arguments and return the relative
|
51
|
+
# difference (-1, 0, 1) like Array#sort would work.
|
44
52
|
def initialize(&block)
|
45
53
|
@filter_lock = Mutex.new
|
46
54
|
@observer_lock = Mutex.new
|
@@ -60,6 +68,8 @@ class Pigeon::Queue
|
|
60
68
|
@tasks = Pigeon::SortedArray.new(&@sort_by)
|
61
69
|
end
|
62
70
|
|
71
|
+
# Returns the contents sorted by the given block. The block will be passed
|
72
|
+
# a single Task and the results are sorted by the return value.
|
63
73
|
def sort_by(&block)
|
64
74
|
raise BlockRequired unless (block_given?)
|
65
75
|
|
@@ -71,18 +81,34 @@ class Pigeon::Queue
|
|
71
81
|
end
|
72
82
|
end
|
73
83
|
|
84
|
+
# Sets up a callback for the queue that will execute the block if new tasks
|
85
|
+
# are added to the queue. If filter_name is specified, this block will be
|
86
|
+
# run for tasks matching that filtered subset.
|
74
87
|
def observe(filter_name = nil, &block)
|
75
88
|
raise BlockRequired unless (block_given?)
|
76
89
|
|
77
90
|
@observer_lock.synchronize do
|
78
91
|
@observers[filter_name] ||= [ ]
|
92
|
+
|
93
|
+
@observers[filter_name] << block
|
79
94
|
end
|
80
95
|
|
81
|
-
@observers[filter_name] << block
|
82
|
-
|
83
96
|
task = assign_next_task(filter_name)
|
84
97
|
end
|
85
98
|
|
99
|
+
# Removes references to the callback function specified. Note that the same
|
100
|
+
# Proc must be passed in, as a block with an identical function will not
|
101
|
+
# be considered equivalent.
|
102
|
+
def remove_observer(filter_name = nil, &block)
|
103
|
+
@observer_lock.synchronize do
|
104
|
+
set = @observers[filter_name]
|
105
|
+
|
106
|
+
set and set.delete(block)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Creates a named filter for the queue using the provided block to select
|
111
|
+
# the tasks which should match.
|
86
112
|
def filter(filter_name, &block)
|
87
113
|
raise BlockRequired unless (block_given?)
|
88
114
|
|
@@ -93,6 +119,7 @@ class Pigeon::Queue
|
|
93
119
|
assign_next_task(filter_name)
|
94
120
|
end
|
95
121
|
|
122
|
+
# Adds a task to the queue.
|
96
123
|
def <<(task)
|
97
124
|
# If there is an insert operation already in progress, put this task in
|
98
125
|
# the backlog for subsequent processing.
|
@@ -159,6 +186,7 @@ class Pigeon::Queue
|
|
159
186
|
task
|
160
187
|
end
|
161
188
|
|
189
|
+
# Iterates over each of the tasks in the queue.
|
162
190
|
def each
|
163
191
|
@filter_lock.synchronize do
|
164
192
|
tasks = @tasks.dup
|
@@ -169,6 +197,9 @@ class Pigeon::Queue
|
|
169
197
|
end
|
170
198
|
end
|
171
199
|
|
200
|
+
# Peeks at the next task in the queue, or if filter_name is provided,
|
201
|
+
# then the next task meeting those filter conditions. An optional block
|
202
|
+
# can also be used to further restrict the qualifying tasks.
|
172
203
|
def peek(filter_name = nil, &block)
|
173
204
|
if (block_given?)
|
174
205
|
@filter_lock.synchronize do
|
@@ -185,6 +216,9 @@ class Pigeon::Queue
|
|
185
216
|
end
|
186
217
|
end
|
187
218
|
|
219
|
+
# Removes all tasks from the queue. If a filter_name is given, then will
|
220
|
+
# only remove tasks matching that filter's conditions. An optional block
|
221
|
+
# can also be used to further restrict the qualifying tasks.
|
188
222
|
def pull(filter_name = nil, &block)
|
189
223
|
unless (block_given?)
|
190
224
|
block = @filters[filter_name]
|
@@ -205,6 +239,11 @@ class Pigeon::Queue
|
|
205
239
|
end
|
206
240
|
end
|
207
241
|
|
242
|
+
# Returns the next task from the queue. If a filter_name is given, then will
|
243
|
+
# only select tasks matching that filter's conditions. An optional block
|
244
|
+
# can also be used to further restrict the qualifying tasks. The task will
|
245
|
+
# be removed from the queue and must be re-inserted if it is to be scheduled
|
246
|
+
# again.
|
208
247
|
def pop(filter_name = nil, &block)
|
209
248
|
@filter_lock.synchronize do
|
210
249
|
task =
|
@@ -232,6 +271,8 @@ class Pigeon::Queue
|
|
232
271
|
end
|
233
272
|
end
|
234
273
|
|
274
|
+
# Claims a task. This is used to indicate that the task will be processed
|
275
|
+
# without having to be inserted into the queue.
|
235
276
|
def claim(task)
|
236
277
|
@filter_lock.synchronize do
|
237
278
|
if (@claimable_task[task])
|
@@ -250,12 +291,16 @@ class Pigeon::Queue
|
|
250
291
|
task
|
251
292
|
end
|
252
293
|
|
294
|
+
# Returns true if the task is queued, false otherwise.
|
253
295
|
def exist?(task)
|
254
296
|
@filter_lock.synchronize do
|
255
297
|
@tasks.exist?(task)
|
256
298
|
end
|
257
299
|
end
|
258
300
|
|
301
|
+
# Returns true if the queue is empty, false otherwise. If filter_name is
|
302
|
+
# given, then will return true if there are no matching tasks, false
|
303
|
+
# otherwise. An optional block can further restrict qualifying tasks.
|
259
304
|
def empty?(filter_name = nil, &block)
|
260
305
|
if (block_given?)
|
261
306
|
@filter_lock.synchronize do
|
@@ -266,6 +311,9 @@ class Pigeon::Queue
|
|
266
311
|
end
|
267
312
|
end
|
268
313
|
|
314
|
+
# Returns the number of entries in the queue. If filter_name is given, then
|
315
|
+
# will return the number of matching tasks. An optional block can further
|
316
|
+
# restrict qualifying tasks.
|
269
317
|
def length(filter_name = nil, &block)
|
270
318
|
filter_proc = @filters[filter_name]
|
271
319
|
|
@@ -276,6 +324,7 @@ class Pigeon::Queue
|
|
276
324
|
alias_method :size, :length
|
277
325
|
alias_method :count, :length
|
278
326
|
|
327
|
+
# Copies the list of queued tasks to a new Array.
|
279
328
|
def to_a
|
280
329
|
@filter_lock.synchronize do
|
281
330
|
@tasks.dup
|
data/lib/pigeon/scheduler.rb
CHANGED
@@ -96,6 +96,12 @@ class Pigeon::Scheduler
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
alias_method :queue_size, :queue_length
|
99
|
+
|
100
|
+
# Returns the number of queues that have been defined, including the default
|
101
|
+
# queue if any.
|
102
|
+
def queue_count
|
103
|
+
@queues.length
|
104
|
+
end
|
99
105
|
|
100
106
|
# Returns the number of processors that are attached to this scheduler.
|
101
107
|
def processors_count
|
data/pigeon.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{pigeon}
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["tadman"]
|
12
|
-
s.date = %q{2010-12-
|
12
|
+
s.date = %q{2010-12-15}
|
13
13
|
s.default_executable = %q{launcher.example}
|
14
14
|
s.description = %q{Pigeon is a simple way to get started building an EventMachine engine that's intended to run as a background job.}
|
15
15
|
s.email = %q{github@tadman.ca}
|
@@ -43,9 +43,11 @@ class PigeonLauncherTest < Test::Unit::TestCase
|
|
43
43
|
|
44
44
|
# FIX: Test `run`
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
if (false)
|
47
|
+
assert_equal 1, triggered[:start]
|
48
|
+
assert_equal 1, triggered[:restart]
|
49
|
+
assert_equal 1, triggered[:status]
|
50
|
+
assert_equal 1, triggered[:stop]
|
51
|
+
end
|
50
52
|
end
|
51
53
|
end
|
@@ -75,6 +75,41 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
75
75
|
queue.empty?
|
76
76
|
end
|
77
77
|
end
|
78
|
+
|
79
|
+
def test_reassigning_queues
|
80
|
+
queue_a = Pigeon::Queue.new
|
81
|
+
queue_b = Pigeon::Queue.new
|
82
|
+
|
83
|
+
processor = Pigeon::Processor.new(queue_a)
|
84
|
+
|
85
|
+
task_a = TaggedTask.new(0)
|
86
|
+
assert !task_a.finished?
|
87
|
+
|
88
|
+
queue_a << task_a
|
89
|
+
|
90
|
+
assert_eventually(1) do
|
91
|
+
task_a.finished?
|
92
|
+
end
|
93
|
+
|
94
|
+
processor.queue = queue_b
|
95
|
+
|
96
|
+
task_b = TaggedTask.new(1)
|
97
|
+
assert !task_b.finished?
|
98
|
+
|
99
|
+
queue_b << task_b
|
100
|
+
|
101
|
+
assert_eventually(1) do
|
102
|
+
task_b.finished?
|
103
|
+
end
|
104
|
+
|
105
|
+
task_c = TaggedTask.new(2)
|
106
|
+
|
107
|
+
queue_a << task_c
|
108
|
+
|
109
|
+
sleep(1)
|
110
|
+
|
111
|
+
assert_equal false, task_c.finished?
|
112
|
+
end
|
78
113
|
|
79
114
|
def test_multiple_processors
|
80
115
|
queue = Pigeon::Queue.new
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 4
|
8
|
-
-
|
9
|
-
version: 0.4.
|
8
|
+
- 3
|
9
|
+
version: 0.4.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- tadman
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-12-
|
17
|
+
date: 2010-12-15 00:00:00 -05:00
|
18
18
|
default_executable: launcher.example
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|