pigeon 0.5.2 → 0.6.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.
- data/LICENSE +1 -1
- data/VERSION +1 -1
- data/lib/pigeon.rb +2 -0
- data/lib/pigeon/engine.rb +6 -2
- data/lib/pigeon/launcher.rb +6 -6
- data/lib/pigeon/processor.rb +3 -3
- data/lib/pigeon/queue.rb +76 -75
- data/lib/pigeon/support.rb +52 -4
- data/pigeon.gemspec +6 -7
- data/test/helper.rb +38 -0
- data/test/unit/pigeon_launcher_test.rb +2 -2
- data/test/unit/pigeon_processor_test.rb +47 -41
- data/test/unit/pigeon_queue_test.rb +157 -115
- metadata +3 -5
data/LICENSE
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/lib/pigeon.rb
CHANGED
data/lib/pigeon/engine.rb
CHANGED
@@ -132,8 +132,12 @@ class Pigeon::Engine
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def self.start(options = nil)
|
135
|
-
|
136
|
-
|
135
|
+
logger = self.engine_logger
|
136
|
+
|
137
|
+
pid = Pigeon::Support.daemonize(logger) do
|
138
|
+
launch({
|
139
|
+
:logger => logger
|
140
|
+
}.merge(options || { }))
|
137
141
|
end
|
138
142
|
|
139
143
|
pid_file.create!(pid)
|
data/lib/pigeon/launcher.rb
CHANGED
@@ -3,23 +3,23 @@ require 'optparse'
|
|
3
3
|
class Pigeon::Launcher
|
4
4
|
# == Class Methods ========================================================
|
5
5
|
|
6
|
-
def self.launch(engine,
|
6
|
+
def self.launch(engine = Pigeon::Engine, *arguments)
|
7
|
+
arguments = %w[ start ] if (arguments.empty?)
|
8
|
+
|
9
|
+
new(engine).handle_args(*arguments)
|
7
10
|
end
|
8
11
|
|
9
12
|
# == Instance Methods =====================================================
|
10
13
|
|
11
14
|
def initialize(with_engine = Pigeon::Engine)
|
12
15
|
@engine = with_engine
|
13
|
-
|
16
|
+
|
17
|
+
yield(self) if (block_given?)
|
14
18
|
end
|
15
19
|
|
16
20
|
def handle_args(*args)
|
17
21
|
op = OptionParser.new
|
18
22
|
|
19
|
-
op.on("-s", "--supervise") do
|
20
|
-
@options[:supervise] = true
|
21
|
-
end
|
22
|
-
|
23
23
|
command = op.parse(*args.flatten).first
|
24
24
|
|
25
25
|
begin
|
data/lib/pigeon/processor.rb
CHANGED
@@ -20,7 +20,7 @@ class Pigeon::Processor
|
|
20
20
|
def initialize(queue = nil, context = nil, &filter)
|
21
21
|
@id = Pigeon::Support.unique_id
|
22
22
|
@lock = Mutex.new
|
23
|
-
@filter = filter
|
23
|
+
@filter = filter
|
24
24
|
@context = context
|
25
25
|
|
26
26
|
if (queue)
|
@@ -40,7 +40,7 @@ class Pigeon::Processor
|
|
40
40
|
if (@queue = queue)
|
41
41
|
@claim = lambda do |task|
|
42
42
|
@lock.synchronize do
|
43
|
-
if (!@task and @filter.call(task))
|
43
|
+
if (!@task and (!@filter or @filter.call(task)))
|
44
44
|
@task = queue.claim(task)
|
45
45
|
|
46
46
|
before_task(@task)
|
@@ -59,7 +59,7 @@ class Pigeon::Processor
|
|
59
59
|
# Returns true if the given task would be accepted by the filter defined
|
60
60
|
# for this processor.
|
61
61
|
def accept?(task)
|
62
|
-
@filter.call(task)
|
62
|
+
!@filter or @filter.call(task)
|
63
63
|
end
|
64
64
|
|
65
65
|
# Returns true if a task is currently being processed, false otherwise.
|
data/lib/pigeon/queue.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
|
1
3
|
class Pigeon::Queue
|
2
4
|
# == Constants ============================================================
|
3
5
|
|
@@ -44,15 +46,13 @@ class Pigeon::Queue
|
|
44
46
|
# and order them, so it should take two arguments and return the relative
|
45
47
|
# difference (-1, 0, 1) like Array#sort would work.
|
46
48
|
def initialize(&block)
|
47
|
-
@filter_lock = Mutex.new
|
48
|
-
@observer_lock = Mutex.new
|
49
|
-
|
50
|
-
@claimable_task = { }
|
51
49
|
@filters = self.class.filters.dup
|
50
|
+
@filters.extend(MonitorMixin)
|
52
51
|
@observers = { }
|
52
|
+
@observers.extend(MonitorMixin)
|
53
|
+
@claimable_task = { }
|
53
54
|
@processors = [ ]
|
54
55
|
@next_task = { }
|
55
|
-
@insert_backlog = [ ]
|
56
56
|
|
57
57
|
if (block_given?)
|
58
58
|
@sort_by = block
|
@@ -69,7 +69,7 @@ class Pigeon::Queue
|
|
69
69
|
raise BlockRequired unless (block_given?)
|
70
70
|
|
71
71
|
@sort_by = block
|
72
|
-
@
|
72
|
+
@filters.synchronize do
|
73
73
|
@tasks = Pigeon::SortedArray.new(&@sort_by) + @tasks
|
74
74
|
|
75
75
|
@next_task = { }
|
@@ -82,10 +82,10 @@ class Pigeon::Queue
|
|
82
82
|
def observe(filter_name = nil, &block)
|
83
83
|
raise BlockRequired unless (block_given?)
|
84
84
|
|
85
|
-
@
|
86
|
-
@observers[filter_name] ||= [ ]
|
85
|
+
@observers.synchronize do
|
86
|
+
set = @observers[filter_name] ||= [ ]
|
87
87
|
|
88
|
-
|
88
|
+
set << block
|
89
89
|
end
|
90
90
|
|
91
91
|
task = assign_next_task(filter_name)
|
@@ -95,7 +95,7 @@ class Pigeon::Queue
|
|
95
95
|
# Proc must be passed in, as a block with an identical function will not
|
96
96
|
# be considered equivalent.
|
97
97
|
def remove_observer(filter_name = nil, &block)
|
98
|
-
@
|
98
|
+
@observers.synchronize do
|
99
99
|
set = @observers[filter_name]
|
100
100
|
|
101
101
|
set and set.delete(block)
|
@@ -104,7 +104,7 @@ class Pigeon::Queue
|
|
104
104
|
|
105
105
|
# Adds a processor to the queue and adds an observer claim method.
|
106
106
|
def add_processor(processor, &claim)
|
107
|
-
@
|
107
|
+
@observers.synchronize do
|
108
108
|
@processors << processor
|
109
109
|
end
|
110
110
|
|
@@ -113,7 +113,7 @@ class Pigeon::Queue
|
|
113
113
|
|
114
114
|
# Removes a processor from the queue and removes an observer claim method.
|
115
115
|
def remove_processor(processor, &claim)
|
116
|
-
@
|
116
|
+
@observers.synchronize do
|
117
117
|
@processors.delete(processor)
|
118
118
|
end
|
119
119
|
|
@@ -125,7 +125,7 @@ class Pigeon::Queue
|
|
125
125
|
def filter(filter_name, &block)
|
126
126
|
raise BlockRequired unless (block_given?)
|
127
127
|
|
128
|
-
@
|
128
|
+
@filters.synchronize do
|
129
129
|
@filters[filter_name] = block
|
130
130
|
end
|
131
131
|
|
@@ -141,69 +141,64 @@ class Pigeon::Queue
|
|
141
141
|
# If there is an insert operation already in progress, put this task in
|
142
142
|
# the backlog for subsequent processing.
|
143
143
|
|
144
|
-
|
145
|
-
|
146
|
-
return task
|
144
|
+
Pigeon::Engine.execute_in_main_thread do
|
145
|
+
self.execute_add_task!(task)
|
147
146
|
end
|
148
147
|
|
149
|
-
|
150
|
-
|
151
|
-
while (active_task) do
|
152
|
-
# Set the claimable task flag for this task since it is not yet in the
|
153
|
-
# actual task queue.
|
154
|
-
@claimable_task[active_task] = true
|
155
|
-
|
156
|
-
unless (@observers.empty?)
|
157
|
-
@observer_lock.synchronize do
|
158
|
-
@observers.each do |filter_name, list|
|
159
|
-
# Check if this task matches the filter restrictions, and if it
|
160
|
-
# does then call the observer chain in order.
|
161
|
-
if (@filters[filter_name].call(active_task))
|
162
|
-
@observers[filter_name].each do |proc|
|
163
|
-
case (proc.arity)
|
164
|
-
when 2
|
165
|
-
proc.call(self, active_task)
|
166
|
-
else
|
167
|
-
proc.call(active_task)
|
168
|
-
end
|
148
|
+
task
|
149
|
+
end
|
169
150
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
151
|
+
def execute_add_task!(task)
|
152
|
+
# Set the claimable task flag for this task since it is not yet in the
|
153
|
+
# actual task queue.
|
154
|
+
@claimable_task[task] = true
|
155
|
+
|
156
|
+
unless (@observers.empty?)
|
157
|
+
@observers.synchronize do
|
158
|
+
@observers.each do |filter_name, list|
|
159
|
+
# Check if this task matches the filter restrictions, and if it
|
160
|
+
# does then call the observer chain in order.
|
161
|
+
if (@filters[filter_name].call(task))
|
162
|
+
@observers[filter_name].each do |proc|
|
163
|
+
case (proc.arity)
|
164
|
+
when 2
|
165
|
+
proc.call(self, task)
|
166
|
+
else
|
167
|
+
proc.call(task)
|
174
168
|
end
|
169
|
+
|
170
|
+
# An observer callback has the opportunity to claim a task,
|
171
|
+
# and if it does, the claimable task flag will be false. Loop
|
172
|
+
# only while the task is claimable.
|
173
|
+
break unless (@claimable_task[task])
|
175
174
|
end
|
176
175
|
end
|
177
176
|
end
|
178
177
|
end
|
178
|
+
end
|
179
179
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
180
|
+
# If this task wasn't claimed by an observer then insert it in the
|
181
|
+
# main task queue.
|
182
|
+
if (@claimable_task.delete(task))
|
183
|
+
@filters.synchronize do
|
184
|
+
@tasks << task
|
185
|
+
|
186
|
+
# Update the next task slots for all of the unassigned filters and
|
187
|
+
# trigger observer callbacks as required.
|
188
|
+
@next_task.each do |filter_name, next_task|
|
189
|
+
next if (next_task)
|
185
190
|
|
186
|
-
|
187
|
-
|
188
|
-
@next_task.each do |filter_name, next_task|
|
189
|
-
next if (next_task)
|
190
|
-
|
191
|
-
if (@filters[filter_name].call(active_task))
|
192
|
-
@next_task[filter_name] = active_task
|
193
|
-
end
|
191
|
+
if (@filters[filter_name].call(task))
|
192
|
+
@next_task[filter_name] = task
|
194
193
|
end
|
195
194
|
end
|
196
195
|
end
|
197
|
-
|
198
|
-
active_task = @insert_backlog.shift
|
199
196
|
end
|
200
|
-
|
201
|
-
task
|
202
197
|
end
|
203
198
|
|
204
199
|
# Iterates over each of the tasks in the queue.
|
205
200
|
def each
|
206
|
-
@
|
201
|
+
@filters.synchronize do
|
207
202
|
tasks = @tasks.dup
|
208
203
|
end
|
209
204
|
|
@@ -217,17 +212,21 @@ class Pigeon::Queue
|
|
217
212
|
# can also be used to further restrict the qualifying tasks.
|
218
213
|
def peek(filter_name = nil, &block)
|
219
214
|
if (block_given?)
|
220
|
-
@
|
215
|
+
@filters.synchronize do
|
221
216
|
@tasks.find(&block)
|
222
217
|
end
|
223
|
-
|
218
|
+
elsif (filter_name)
|
224
219
|
@next_task[filter_name] ||= begin
|
225
|
-
@
|
220
|
+
@filters.synchronize do
|
226
221
|
filter_proc = @filters[filter_name]
|
227
222
|
|
228
223
|
filter_proc and @tasks.find(&filter_proc)
|
229
224
|
end
|
230
225
|
end
|
226
|
+
else
|
227
|
+
@filters.synchronize do
|
228
|
+
@tasks.first
|
229
|
+
end
|
231
230
|
end
|
232
231
|
end
|
233
232
|
|
@@ -235,13 +234,13 @@ class Pigeon::Queue
|
|
235
234
|
# only remove tasks matching that filter's conditions. An optional block
|
236
235
|
# can also be used to further restrict the qualifying tasks.
|
237
236
|
def pull(filter_name = nil, &block)
|
238
|
-
|
237
|
+
if (!block_given? and filter_name)
|
239
238
|
block = @filters[filter_name]
|
240
239
|
end
|
241
240
|
|
242
|
-
@
|
243
|
-
tasks = @tasks.select(&block)
|
244
|
-
|
241
|
+
@filters.synchronize do
|
242
|
+
tasks = block ? @tasks.select(&block) : @tasks
|
243
|
+
|
245
244
|
@tasks -= tasks
|
246
245
|
|
247
246
|
@next_task.each do |filter_name, next_task|
|
@@ -260,16 +259,18 @@ class Pigeon::Queue
|
|
260
259
|
# be removed from the queue and must be re-inserted if it is to be scheduled
|
261
260
|
# again.
|
262
261
|
def pop(filter_name = nil, &block)
|
263
|
-
@
|
262
|
+
@filters.synchronize do
|
264
263
|
task =
|
265
264
|
if (block_given?)
|
266
265
|
@tasks.find(&block)
|
267
|
-
|
266
|
+
elsif (filter_name)
|
268
267
|
@next_task[filter_name] || begin
|
269
268
|
filter_proc = @filters[filter_name]
|
270
269
|
|
271
270
|
filter_proc and @tasks.find(&filter_proc)
|
272
271
|
end
|
272
|
+
else
|
273
|
+
@tasks.first
|
273
274
|
end
|
274
275
|
|
275
276
|
if (task)
|
@@ -289,7 +290,7 @@ class Pigeon::Queue
|
|
289
290
|
# Claims a task. This is used to indicate that the task will be processed
|
290
291
|
# without having to be inserted into the queue.
|
291
292
|
def claim(task)
|
292
|
-
@
|
293
|
+
@filters.synchronize do
|
293
294
|
if (@claimable_task[task])
|
294
295
|
@claimable_task[task] = false
|
295
296
|
elsif (@tasks.delete(task))
|
@@ -307,9 +308,9 @@ class Pigeon::Queue
|
|
307
308
|
end
|
308
309
|
|
309
310
|
# Returns true if the task is queued, false otherwise.
|
310
|
-
def
|
311
|
-
@
|
312
|
-
@tasks.
|
311
|
+
def include?(task)
|
312
|
+
@filters.synchronize do
|
313
|
+
@tasks.include?(task)
|
313
314
|
end
|
314
315
|
end
|
315
316
|
|
@@ -318,7 +319,7 @@ class Pigeon::Queue
|
|
318
319
|
# otherwise. An optional block can further restrict qualifying tasks.
|
319
320
|
def empty?(filter_name = nil, &block)
|
320
321
|
if (block_given?)
|
321
|
-
@
|
322
|
+
@filters.synchronize do
|
322
323
|
!@tasks.find(&block)
|
323
324
|
end
|
324
325
|
else
|
@@ -332,7 +333,7 @@ class Pigeon::Queue
|
|
332
333
|
def length(filter_name = nil, &block)
|
333
334
|
filter_proc = @filters[filter_name]
|
334
335
|
|
335
|
-
@
|
336
|
+
@filters.synchronize do
|
336
337
|
filter_proc ? @tasks.count(&filter_proc) : nil
|
337
338
|
end
|
338
339
|
end
|
@@ -341,7 +342,7 @@ class Pigeon::Queue
|
|
341
342
|
|
342
343
|
# Copies the list of queued tasks to a new Array.
|
343
344
|
def to_a
|
344
|
-
@
|
345
|
+
@filters.synchronize do
|
345
346
|
@tasks.dup
|
346
347
|
end
|
347
348
|
end
|
@@ -356,7 +357,7 @@ protected
|
|
356
357
|
return task
|
357
358
|
end
|
358
359
|
|
359
|
-
@
|
360
|
+
@filters.synchronize do
|
360
361
|
@next_task[filter_name] ||= @tasks.find(&filter)
|
361
362
|
end
|
362
363
|
end
|
data/lib/pigeon/support.rb
CHANGED
@@ -4,22 +4,70 @@ module Pigeon::Support
|
|
4
4
|
# Uses the double-fork method to create a fully detached background
|
5
5
|
# process. Returns the process ID of the created process. May throw an
|
6
6
|
# exception if these processes could not be created.
|
7
|
-
def daemonize
|
7
|
+
def daemonize(logger = nil)
|
8
|
+
delay = 10
|
8
9
|
rfd, wfd = IO.pipe
|
9
10
|
|
10
11
|
forked_pid = fork do
|
11
|
-
|
12
|
-
|
12
|
+
rfd.close
|
13
|
+
|
14
|
+
supervisor_pid = fork do
|
15
|
+
relaunch = true
|
16
|
+
|
17
|
+
while (relaunch)
|
18
|
+
daemon_pid = fork do
|
19
|
+
begin
|
20
|
+
yield
|
21
|
+
rescue Object => e
|
22
|
+
if (logger)
|
23
|
+
logger.error("Terminated with Exception: [#{e.class}] #{e}")
|
24
|
+
logger.error(e.backtrace.join("\n"))
|
25
|
+
|
26
|
+
Thread.list.each do |thread|
|
27
|
+
logger.error("Stack trace of current threads")
|
28
|
+
logger.error(thread.inspect)
|
29
|
+
|
30
|
+
if (thread.backtrace)
|
31
|
+
logger.error("\t" + thread.backtrace.join("\n\t"))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
Process.wait(daemon_pid)
|
40
|
+
|
41
|
+
# A non-zero exit status indicates some sort of error, so the
|
42
|
+
# process will be relaunched after a short delay.
|
43
|
+
relaunch = ($? != 0)
|
44
|
+
|
45
|
+
rescue Interrupt
|
46
|
+
relaunch = false
|
47
|
+
end
|
48
|
+
|
49
|
+
if (relaunch)
|
50
|
+
logger.info("Will relaunch in %d seconds" % delay)
|
51
|
+
|
52
|
+
sleep(delay)
|
53
|
+
else
|
54
|
+
logger.info("Terminated normally")
|
55
|
+
end
|
56
|
+
end
|
13
57
|
end
|
58
|
+
|
59
|
+
wfd.puts(supervisor_pid)
|
14
60
|
|
15
|
-
wfd.puts daemon_pid
|
16
61
|
wfd.flush
|
17
62
|
wfd.close
|
18
63
|
end
|
19
64
|
|
65
|
+
wfd.close
|
66
|
+
|
20
67
|
Process.wait(forked_pid)
|
21
68
|
|
22
69
|
daemon_pid = rfd.readline
|
70
|
+
rfd.close
|
23
71
|
|
24
72
|
daemon_pid.to_i
|
25
73
|
end
|
data/pigeon.gemspec
CHANGED
@@ -5,15 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{pigeon}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-
|
13
|
-
s.default_executable = %q{launcher.example}
|
11
|
+
s.authors = [%q{tadman}]
|
12
|
+
s.date = %q{2011-05-12}
|
14
13
|
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
14
|
s.email = %q{github@tadman.ca}
|
16
|
-
s.executables = [
|
15
|
+
s.executables = [%q{launcher.example}]
|
17
16
|
s.extra_rdoc_files = [
|
18
17
|
"LICENSE",
|
19
18
|
"README.rdoc"
|
@@ -53,8 +52,8 @@ Gem::Specification.new do |s|
|
|
53
52
|
"test/unit/pigeon_test.rb"
|
54
53
|
]
|
55
54
|
s.homepage = %q{http://github.com/twg/pigeon}
|
56
|
-
s.require_paths = [
|
57
|
-
s.rubygems_version = %q{1.
|
55
|
+
s.require_paths = [%q{lib}]
|
56
|
+
s.rubygems_version = %q{1.8.1}
|
58
57
|
s.summary = %q{Simple daemonized EventMachine engine framework with plug-in support}
|
59
58
|
s.test_files = [
|
60
59
|
"test/helper.rb",
|
data/test/helper.rb
CHANGED
@@ -34,4 +34,42 @@ class Test::Unit::TestCase
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
def engine
|
39
|
+
exception = nil
|
40
|
+
|
41
|
+
@engine_thread = Thread.new do
|
42
|
+
Thread.abort_on_exception = true
|
43
|
+
|
44
|
+
# Create a thread for the engine to run on
|
45
|
+
begin
|
46
|
+
Pigeon::Engine.launch do |new_engine|
|
47
|
+
@engine = new_engine
|
48
|
+
|
49
|
+
Thread.new do
|
50
|
+
# Execute the test code in a separate thread to avoid blocking
|
51
|
+
# the EventMachine loop.
|
52
|
+
begin
|
53
|
+
yield
|
54
|
+
rescue Object => exception
|
55
|
+
ensure
|
56
|
+
begin
|
57
|
+
@engine.terminate
|
58
|
+
rescue Object
|
59
|
+
# Shutting down may trigger an exception from time to time
|
60
|
+
# if the engine itself has failed.
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rescue Object => exception
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@engine_thread.join
|
70
|
+
|
71
|
+
if (exception)
|
72
|
+
raise exception
|
73
|
+
end
|
74
|
+
end
|
37
75
|
end
|
@@ -4,10 +4,10 @@ class PigeonLauncherTest < Test::Unit::TestCase
|
|
4
4
|
def test_default_launcher
|
5
5
|
pid = Pigeon::Launcher.launch
|
6
6
|
|
7
|
-
assert pid
|
7
|
+
assert pid, "PID should be returned from launcher call"
|
8
8
|
assert Pigeon::Engine.running?
|
9
9
|
|
10
|
-
Pigeon::
|
10
|
+
Pigeon::Engine.stop
|
11
11
|
|
12
12
|
assert !Pigeon::Engine.running?
|
13
13
|
end
|
@@ -77,72 +77,78 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def test_reassigning_queues
|
80
|
-
|
81
|
-
|
80
|
+
engine do
|
81
|
+
queue_a = Pigeon::Queue.new
|
82
|
+
queue_b = Pigeon::Queue.new
|
82
83
|
|
83
|
-
|
84
|
+
processor = Pigeon::Processor.new(queue_a)
|
84
85
|
|
85
|
-
|
86
|
-
|
86
|
+
task_a = TaggedTask.new(0)
|
87
|
+
assert !task_a.finished?
|
87
88
|
|
88
|
-
|
89
|
+
queue_a << task_a
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
assert_eventually(1) do
|
92
|
+
task_a.finished?
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
+
processor.queue = queue_b
|
95
96
|
|
96
|
-
|
97
|
-
|
97
|
+
task_b = TaggedTask.new(1)
|
98
|
+
assert !task_b.finished?
|
98
99
|
|
99
|
-
|
100
|
+
queue_b << task_b
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
assert_eventually(1) do
|
103
|
+
task_b.finished?
|
104
|
+
end
|
104
105
|
|
105
|
-
|
106
|
+
task_c = TaggedTask.new(2)
|
106
107
|
|
107
|
-
|
108
|
+
queue_a << task_c
|
108
109
|
|
109
|
-
|
110
|
+
sleep(1)
|
110
111
|
|
111
|
-
|
112
|
+
assert_equal false, task_c.finished?
|
113
|
+
end
|
112
114
|
end
|
113
115
|
|
114
116
|
def test_can_unassign_queue_from_processor
|
115
|
-
|
116
|
-
|
117
|
+
engine do
|
118
|
+
queue = Pigeon::Queue.new
|
119
|
+
processor = Pigeon::Processor.new(queue)
|
117
120
|
|
118
|
-
|
119
|
-
|
121
|
+
assert_equal queue, processor.queue
|
122
|
+
assert_equal [ processor ], queue.processors
|
120
123
|
|
121
|
-
|
124
|
+
processor.queue = nil
|
122
125
|
|
123
|
-
|
124
|
-
|
126
|
+
assert_equal nil, processor.queue
|
127
|
+
assert_equal [ ], queue.processors
|
128
|
+
end
|
125
129
|
end
|
126
130
|
|
127
131
|
def test_multiple_processors
|
128
|
-
|
129
|
-
|
132
|
+
engine do
|
133
|
+
queue = Pigeon::Queue.new
|
134
|
+
count = 10000
|
130
135
|
|
131
|
-
|
132
|
-
|
133
|
-
|
136
|
+
count.times do |n|
|
137
|
+
queue << TaggedTask.new(n)
|
138
|
+
end
|
134
139
|
|
135
|
-
|
140
|
+
assert_equal count, queue.length
|
136
141
|
|
137
|
-
|
138
|
-
|
139
|
-
|
142
|
+
processors = (0..9).to_a.collect do
|
143
|
+
Pigeon::Processor.new(queue)
|
144
|
+
end
|
140
145
|
|
141
|
-
|
142
|
-
|
143
|
-
|
146
|
+
assert_eventually(10) do
|
147
|
+
queue.empty?
|
148
|
+
end
|
144
149
|
|
145
|
-
|
146
|
-
|
150
|
+
assert_equal 0, processors.select(&:task?).length
|
151
|
+
assert_equal 0, queue.length
|
152
|
+
end
|
147
153
|
end
|
148
154
|
end
|
@@ -13,16 +13,6 @@ class PigeonQueueTest < Test::Unit::TestCase
|
|
13
13
|
"<#{@tag}>"
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
17
|
-
def setup
|
18
|
-
@engine = Pigeon::Engine.new
|
19
|
-
|
20
|
-
Pigeon::Engine.register_engine(@engine)
|
21
|
-
end
|
22
|
-
|
23
|
-
def teardown
|
24
|
-
Pigeon::Engine.unregister_engine(@engine)
|
25
|
-
end
|
26
16
|
|
27
17
|
def test_empty_state
|
28
18
|
queue = Pigeon::Queue.new
|
@@ -36,170 +26,222 @@ class PigeonQueueTest < Test::Unit::TestCase
|
|
36
26
|
end
|
37
27
|
|
38
28
|
def test_cycling
|
39
|
-
|
29
|
+
engine do
|
30
|
+
queue = Pigeon::Queue.new
|
40
31
|
|
41
|
-
|
32
|
+
task = Pigeon::Task.new
|
42
33
|
|
43
|
-
|
34
|
+
assert_equal task, queue << task
|
35
|
+
|
36
|
+
assert_eventually do
|
37
|
+
queue.peek == task
|
38
|
+
end
|
44
39
|
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
assert queue.peek
|
41
|
+
assert_equal 1, queue.length
|
42
|
+
assert !queue.empty?
|
48
43
|
|
49
|
-
|
44
|
+
found_task = queue.pop
|
50
45
|
|
51
|
-
|
46
|
+
assert_equal task, found_task
|
52
47
|
|
53
|
-
|
54
|
-
|
48
|
+
assert_equal 0, queue.length
|
49
|
+
assert queue.empty?
|
50
|
+
end
|
55
51
|
end
|
56
52
|
|
57
53
|
def test_filtering
|
58
|
-
|
54
|
+
engine do
|
55
|
+
queue = Pigeon::Queue.new
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
tasks = (0..9).to_a.collect do |n|
|
58
|
+
queue << TaggedTask.new(n)
|
59
|
+
end
|
60
|
+
|
61
|
+
assert_eventually do
|
62
|
+
queue.length == 10
|
63
|
+
end
|
63
64
|
|
64
|
-
|
65
|
+
assert_equal (0..9).to_a, tasks.to_a.collect(&:tag)
|
65
66
|
|
66
|
-
|
67
|
+
assert_equal tasks[0], queue.peek
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
selected_task = queue.peek do |task|
|
70
|
+
task.tag > 0
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
+
assert_equal tasks[1], selected_task
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
75
|
+
queue.filter(:over_7) do |task|
|
76
|
+
task.tag > 7
|
77
|
+
end
|
77
78
|
|
78
|
-
|
79
|
-
|
79
|
+
assert_equal tasks[8], queue.peek(:over_7)
|
80
|
+
assert_equal 2, queue.length(:over_7)
|
80
81
|
|
81
|
-
|
82
|
+
pulled_task = queue.pop(:over_7)
|
82
83
|
|
83
|
-
|
84
|
+
assert_equal 9, queue.length
|
84
85
|
|
85
|
-
|
86
|
-
|
86
|
+
assert_equal tasks[9], queue.peek(:over_7)
|
87
|
+
assert_equal 1, queue.length(:over_7)
|
87
88
|
|
88
|
-
|
89
|
+
queue.pop(:over_7)
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
assert_equal nil, queue.peek(:over_7)
|
92
|
+
assert_equal 0, queue.length(:over_7)
|
93
|
+
assert_equal true, queue.empty?(:over_7)
|
93
94
|
|
94
|
-
|
95
|
+
new_task = queue << TaggedTask.new(10)
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
97
|
+
assert_eventually do
|
98
|
+
queue.include?(new_task)
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_equal new_task, queue.peek(:over_7)
|
102
|
+
assert_equal 1, queue.length(:over_7)
|
103
|
+
assert_equal false, queue.empty?(:over_7)
|
99
104
|
|
100
|
-
|
105
|
+
queue.claim(new_task)
|
101
106
|
|
102
|
-
|
103
|
-
|
104
|
-
|
107
|
+
assert_equal nil, queue.peek(:over_7)
|
108
|
+
assert_equal 0, queue.length(:over_7)
|
109
|
+
assert_equal true, queue.empty?(:over_7)
|
110
|
+
end
|
105
111
|
end
|
106
112
|
|
107
113
|
def test_observe
|
108
|
-
|
114
|
+
engine do
|
115
|
+
queue = Pigeon::Queue.new
|
109
116
|
|
110
|
-
|
111
|
-
|
112
|
-
|
117
|
+
tasks = (0..9).to_a.collect do |n|
|
118
|
+
queue << TaggedTask.new(n)
|
119
|
+
end
|
113
120
|
|
114
|
-
|
115
|
-
|
116
|
-
|
121
|
+
queue.filter(:odd) do |task|
|
122
|
+
task.tag % 2 == 1
|
123
|
+
end
|
124
|
+
|
125
|
+
assert_eventually(2) do
|
126
|
+
queue.length == 10
|
127
|
+
end
|
117
128
|
|
118
|
-
|
119
|
-
|
129
|
+
assert_equal tasks[1], queue.peek(:odd)
|
130
|
+
assert_equal 5, queue.length(:odd)
|
120
131
|
|
121
|
-
|
132
|
+
assert_equal [ tasks[1], tasks[3], tasks[5], tasks[7], tasks[9] ], queue.pull(:odd)
|
122
133
|
|
123
|
-
|
124
|
-
|
134
|
+
assert_equal 5, queue.length
|
135
|
+
assert_equal 0, queue.length(:odd)
|
125
136
|
|
126
|
-
|
137
|
+
added_odd = nil
|
127
138
|
|
128
|
-
|
129
|
-
|
130
|
-
|
139
|
+
queue.observe(:odd) do |task|
|
140
|
+
added_odd = task
|
141
|
+
end
|
131
142
|
|
132
|
-
|
143
|
+
new_task = queue << TaggedTask.new(10)
|
144
|
+
|
145
|
+
assert_eventually(2) do
|
146
|
+
queue.include?(new_task)
|
147
|
+
end
|
133
148
|
|
134
|
-
|
149
|
+
assert_equal nil, added_odd
|
135
150
|
|
136
|
-
|
151
|
+
odd_1 = queue << TaggedTask.new(11)
|
152
|
+
|
153
|
+
assert_eventually(2) do
|
154
|
+
queue.include?(odd_1)
|
155
|
+
end
|
137
156
|
|
138
|
-
|
157
|
+
assert_equal odd_1, added_odd
|
139
158
|
|
140
|
-
|
141
|
-
|
159
|
+
claimed_task = nil
|
160
|
+
has_run = false
|
142
161
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
162
|
+
queue.observe(:odd) do |task|
|
163
|
+
claimed_task = queue.claim(task)
|
164
|
+
has_run = true
|
165
|
+
end
|
147
166
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
167
|
+
# Observer callbacks are not triggered on existing data, only on new
|
168
|
+
# insertions.
|
169
|
+
assert_equal false, has_run
|
170
|
+
assert_equal nil, claimed_task
|
171
|
+
assert_equal 7, queue.length
|
172
|
+
assert_equal 1, queue.length(:odd)
|
173
|
+
|
174
|
+
new_task = queue << TaggedTask.new(12)
|
175
|
+
|
176
|
+
assert_eventually(2) do
|
177
|
+
queue.include?(new_task)
|
178
|
+
end
|
156
179
|
|
157
|
-
|
158
|
-
|
159
|
-
|
180
|
+
assert_equal nil, claimed_task
|
181
|
+
assert_equal 8, queue.length
|
182
|
+
assert_equal 1, queue.length(:odd)
|
160
183
|
|
161
|
-
|
184
|
+
odd_2 = queue << TaggedTask.new(13)
|
185
|
+
|
186
|
+
assert_eventually(2) do
|
187
|
+
claimed_task == odd_2
|
188
|
+
end
|
162
189
|
|
163
|
-
|
164
|
-
|
165
|
-
|
190
|
+
# Adding a task that matches the filter triggers the callback.
|
191
|
+
assert_equal odd_2, claimed_task
|
192
|
+
assert_equal true, has_run
|
166
193
|
|
167
|
-
|
168
|
-
|
194
|
+
# Clear out all of the odd entries.
|
195
|
+
queue.pull(:odd)
|
169
196
|
|
170
|
-
|
171
|
-
|
197
|
+
claimed_task = nil
|
198
|
+
has_run = false
|
172
199
|
|
173
|
-
|
200
|
+
new_task = queue << TaggedTask.new(14)
|
201
|
+
|
202
|
+
assert_eventually(2) do
|
203
|
+
queue.include?(new_task)
|
204
|
+
end
|
174
205
|
|
175
|
-
|
176
|
-
|
206
|
+
assert_equal nil, claimed_task
|
207
|
+
assert_equal false, has_run
|
177
208
|
|
178
|
-
|
209
|
+
odd_2 = queue << TaggedTask.new(15)
|
210
|
+
|
211
|
+
assert_eventually(2) do
|
212
|
+
odd_2 == claimed_task
|
213
|
+
end
|
179
214
|
|
180
|
-
|
181
|
-
|
215
|
+
assert_equal odd_2, claimed_task
|
216
|
+
assert_equal true, has_run
|
182
217
|
|
183
|
-
|
184
|
-
|
218
|
+
assert_equal 8, queue.length
|
219
|
+
assert_equal 0, queue.length(:odd)
|
220
|
+
end
|
185
221
|
end
|
186
222
|
|
187
223
|
def test_can_add_during_observe
|
188
|
-
|
224
|
+
engine do
|
225
|
+
queue = Pigeon::Queue.new
|
189
226
|
|
190
|
-
|
191
|
-
|
192
|
-
|
227
|
+
queue.observe do |task|
|
228
|
+
if (task.tag < 10)
|
229
|
+
queue.claim(task)
|
193
230
|
|
194
|
-
|
231
|
+
queue << TaggedTask.new(task.tag + 1)
|
232
|
+
end
|
195
233
|
end
|
196
|
-
end
|
197
234
|
|
198
|
-
|
235
|
+
new_task = queue << TaggedTask.new(0)
|
236
|
+
|
237
|
+
assert_eventually(2) do
|
238
|
+
queue.peek
|
239
|
+
end
|
199
240
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
241
|
+
assert queue.peek
|
242
|
+
assert_equal 10, queue.peek.tag
|
243
|
+
assert_equal 1, queue.length
|
244
|
+
assert queue.peek
|
245
|
+
end
|
204
246
|
end
|
205
247
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: pigeon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.6.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- tadman
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable: launcher.example
|
13
|
+
date: 2011-05-12 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: eventmachine
|
@@ -66,7 +65,6 @@ files:
|
|
66
65
|
- test/unit/pigeon_sorted_array_test.rb
|
67
66
|
- test/unit/pigeon_task_test.rb
|
68
67
|
- test/unit/pigeon_test.rb
|
69
|
-
has_rdoc: true
|
70
68
|
homepage: http://github.com/twg/pigeon
|
71
69
|
licenses: []
|
72
70
|
|
@@ -90,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
88
|
requirements: []
|
91
89
|
|
92
90
|
rubyforge_project:
|
93
|
-
rubygems_version: 1.
|
91
|
+
rubygems_version: 1.8.1
|
94
92
|
signing_key:
|
95
93
|
specification_version: 3
|
96
94
|
summary: Simple daemonized EventMachine engine framework with plug-in support
|