workers 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 +4 -4
- data/.travis.yml +4 -0
- data/Gemfile +2 -1
- data/README.md +26 -3
- data/Rakefile +7 -10
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/workers.rb +3 -2
- data/lib/workers/bucket_scheduler.rb +4 -8
- data/lib/workers/event.rb +1 -1
- data/lib/workers/exceptions.rb +10 -0
- data/lib/workers/helpers.rb +1 -1
- data/lib/workers/log_proxy.rb +5 -5
- data/lib/workers/periodic_timer.rb +1 -1
- data/lib/workers/pool.rb +20 -21
- data/lib/workers/scheduler.rb +8 -8
- data/lib/workers/task.rb +7 -7
- data/lib/workers/task_group.rb +11 -11
- data/lib/workers/timer.rb +7 -8
- data/lib/workers/version.rb +1 -1
- data/lib/workers/worker.rb +48 -24
- data/workers.gemspec +18 -13
- metadata +54 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07932a16f900199549ba88c4eb68a9aee5bf79f3
|
4
|
+
data.tar.gz: 697e7518ffaab1a98fa034aa309b2076392b47d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b7ef7a498830b6d6eff47fe5068abd2180b317ddc80cd3f6d4073dd0c7defcb6d74db05664f08e757a314674c3ee750d34c2327189408d92c5ba429b7796109
|
7
|
+
data.tar.gz: 5559d5cd379dd854e4f1df50ecf8e3fdb8538af9a215f972dc65dd4434eb6caeeae803115687acd8a66c08fddb377e4efd018f45642e885f00459f53f700dc45
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
-
# Workers
|
1
|
+
# Workers [](https://travis-ci.org/chadrem/workers) [](https://coveralls.io/github/chadrem/workers?branch=master)
|
2
2
|
|
3
3
|
Workers is a Ruby gem for performing work in background threads.
|
4
4
|
Design goals include high performance, low latency, simple API, customizability, and multi-layered architecture.
|
5
5
|
It provides a number of simple to use classes that solve a wide range of concurrency problems.
|
6
6
|
It is used by [Tribe](https://github.com/chadrem/tribe "Tribe") to implement event-driven actors.
|
7
7
|
|
8
|
+
## Contents
|
9
|
+
|
10
|
+
- [Installation](#installation)
|
11
|
+
- [Parallel Map](#parallel-map)
|
12
|
+
- [Tasks](#tasks)
|
13
|
+
- [Workers](#workers)
|
14
|
+
- [Pool](#pools)
|
15
|
+
- [Timers](#timers)
|
16
|
+
- [Schedulers](#schedulers)
|
17
|
+
- [Concurrency and performance](#concurrency-and-performance)
|
18
|
+
- [Contributing](#contributing)
|
19
|
+
|
8
20
|
## Installation
|
9
21
|
|
10
22
|
Add this line to your application's Gemfile:
|
@@ -175,7 +187,7 @@ Note that you must handle exceptions yourself since you are working directly wit
|
|
175
187
|
|
176
188
|
# Create a single worker.
|
177
189
|
worker = Workers::Worker.new
|
178
|
-
|
190
|
+
|
179
191
|
# Perform some work in the background.
|
180
192
|
25.times do |i|
|
181
193
|
worker.perform do
|
@@ -187,7 +199,7 @@ Note that you must handle exceptions yourself since you are working directly wit
|
|
187
199
|
end
|
188
200
|
end
|
189
201
|
end
|
190
|
-
|
202
|
+
|
191
203
|
# Tell the worker to shutdown.
|
192
204
|
worker.shutdown
|
193
205
|
|
@@ -294,6 +306,17 @@ You can create additional schedulers as necessary:
|
|
294
306
|
:pool => Workers::Pool.new # The workers pool used to execute timer callbacks.
|
295
307
|
)
|
296
308
|
|
309
|
+
#### Bucket Schedulers
|
310
|
+
|
311
|
+
The Bucket scheduler class is a specialized scheduler designed to work around lock contention.
|
312
|
+
This is accomplished by using many pools (100 by default) each with a small number of workers (1 by default).
|
313
|
+
Timers are assigned to a scheduler by their ````hash```` value.
|
314
|
+
Most users will never need to use this class, but it is documented here for completeness.
|
315
|
+
Both the number of buckets and the number of workers assigned to each bucket are configurable.
|
316
|
+
|
317
|
+
# Create a bucket scheduler.
|
318
|
+
scheduler = Workers::BucketScheduler.new
|
319
|
+
|
297
320
|
## Concurrency and performance
|
298
321
|
|
299
322
|
Workers is tested with both JRuby and MRI (C Ruby).
|
data/Rakefile
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require 'workers'
|
8
|
-
require 'irb'
|
9
|
-
|
10
|
-
ARGV.clear
|
11
|
-
|
12
|
-
IRB.start
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
13
8
|
end
|
9
|
+
|
10
|
+
task :default => :test
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "workers"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/workers.rb
CHANGED
@@ -3,6 +3,7 @@ require 'monitor'
|
|
3
3
|
require 'set'
|
4
4
|
|
5
5
|
require 'workers/version'
|
6
|
+
require 'workers/exceptions'
|
6
7
|
require 'workers/helpers'
|
7
8
|
require 'workers/worker'
|
8
9
|
require 'workers/pool'
|
@@ -18,7 +19,7 @@ require 'workers/task_group'
|
|
18
19
|
module Workers
|
19
20
|
def self.pool
|
20
21
|
lock do
|
21
|
-
|
22
|
+
@pool ||= Workers::Pool.new
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
@@ -31,7 +32,7 @@ module Workers
|
|
31
32
|
|
32
33
|
def self.scheduler
|
33
34
|
lock do
|
34
|
-
|
35
|
+
@scheduler ||= Workers::Scheduler.new
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
@@ -18,29 +18,25 @@ module Workers
|
|
18
18
|
def schedule(timer)
|
19
19
|
@schedulers[timer.hash % @options[:bucket_size]].schedule(timer)
|
20
20
|
|
21
|
-
|
21
|
+
nil
|
22
22
|
end
|
23
23
|
|
24
24
|
def unschedule(timer)
|
25
25
|
@schedulers[timer.hash % @options[:bucket_size]].unschedule(timer)
|
26
26
|
|
27
|
-
|
27
|
+
nil
|
28
28
|
end
|
29
29
|
|
30
30
|
def wakeup
|
31
31
|
@schedulers.each { |s| s.wakeup }
|
32
32
|
|
33
|
-
|
33
|
+
nil
|
34
34
|
end
|
35
35
|
|
36
36
|
def dispose
|
37
37
|
@schedulers.each { |s| s.dispose }
|
38
38
|
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
def alive?
|
43
|
-
return @schedulers.all? { |s| s.alive? }
|
39
|
+
nil
|
44
40
|
end
|
45
41
|
end
|
46
42
|
end
|
data/lib/workers/event.rb
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Workers
|
2
|
+
class WorkersError < RuntimeError; end
|
3
|
+
class UnknownEventError < WorkersError; end
|
4
|
+
class JoinError < WorkersError; end
|
5
|
+
class FailedTaskError < WorkersError; end
|
6
|
+
class InvalidStateError < WorkersError; end
|
7
|
+
class MaxTriesError < WorkersError; end
|
8
|
+
class MissingCallbackError < WorkersError; end
|
9
|
+
class PoolSizeError < WorkersError; end
|
10
|
+
end
|
data/lib/workers/helpers.rb
CHANGED
data/lib/workers/log_proxy.rb
CHANGED
@@ -5,31 +5,31 @@ module Workers
|
|
5
5
|
def initialize(logger)
|
6
6
|
@logger = logger.is_a?(Workers::LogProxy) ? logger.logger : logger
|
7
7
|
|
8
|
-
|
8
|
+
nil
|
9
9
|
end
|
10
10
|
|
11
11
|
def debug(msg)
|
12
12
|
@logger.debug(msg) if @logger
|
13
13
|
|
14
|
-
|
14
|
+
nil
|
15
15
|
end
|
16
16
|
|
17
17
|
def info(msg)
|
18
18
|
@logger.info(msg) if @logger
|
19
19
|
|
20
|
-
|
20
|
+
nil
|
21
21
|
end
|
22
22
|
|
23
23
|
def warn(msg)
|
24
24
|
@logger.warn(msg) if @logger
|
25
25
|
|
26
|
-
|
26
|
+
nil
|
27
27
|
end
|
28
28
|
|
29
29
|
def error(msg)
|
30
30
|
@logger.error(msg) if @logger
|
31
31
|
|
32
|
-
|
32
|
+
nil
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
data/lib/workers/pool.rb
CHANGED
@@ -11,22 +11,23 @@ module Workers
|
|
11
11
|
@lock = Monitor.new
|
12
12
|
@workers = Set.new
|
13
13
|
@size = 0
|
14
|
+
@exception_callback = options[:on_exception]
|
14
15
|
|
15
16
|
expand(options[:size] || Workers::Pool::DEFAULT_POOL_SIZE)
|
16
17
|
|
17
|
-
|
18
|
+
nil
|
18
19
|
end
|
19
20
|
|
20
21
|
def enqueue(command, data = nil)
|
21
22
|
@input_queue.push(Event.new(command, data))
|
22
23
|
|
23
|
-
|
24
|
+
nil
|
24
25
|
end
|
25
26
|
|
26
27
|
def perform(&block)
|
27
28
|
enqueue(:perform, block)
|
28
29
|
|
29
|
-
|
30
|
+
nil
|
30
31
|
end
|
31
32
|
|
32
33
|
def shutdown(&block)
|
@@ -36,7 +37,7 @@ module Workers
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
+
nil
|
40
41
|
end
|
41
42
|
|
42
43
|
def join(max_wait = nil)
|
@@ -44,42 +45,40 @@ module Workers
|
|
44
45
|
@workers.clear
|
45
46
|
@size = 0
|
46
47
|
|
47
|
-
|
48
|
+
results
|
48
49
|
end
|
49
50
|
|
50
|
-
def dispose
|
51
|
-
|
52
|
-
|
53
|
-
join
|
54
|
-
end
|
55
|
-
|
56
|
-
return nil
|
51
|
+
def dispose(max_wait = nil)
|
52
|
+
shutdown
|
53
|
+
join(max_wait)
|
57
54
|
end
|
58
55
|
|
59
56
|
def inspect
|
60
|
-
|
57
|
+
"#<#{self.class.to_s}:0x#{(object_id << 1).to_s(16)} size=#{size}>"
|
61
58
|
end
|
62
59
|
|
63
60
|
def size
|
64
61
|
@lock.synchronize do
|
65
|
-
|
62
|
+
@size
|
66
63
|
end
|
67
64
|
end
|
68
65
|
|
69
66
|
def expand(count)
|
70
67
|
@lock.synchronize do
|
71
68
|
count.times do
|
72
|
-
|
73
|
-
|
69
|
+
worker = @worker_class.new(:input_queue => @input_queue, :die_on_exception => false,
|
70
|
+
:on_exception => @exception_callback, :logger => @logger)
|
71
|
+
@workers << worker
|
72
|
+
@size += 1
|
74
73
|
end
|
75
74
|
end
|
76
75
|
|
77
|
-
|
76
|
+
nil
|
78
77
|
end
|
79
78
|
|
80
79
|
def contract(count, &block)
|
81
80
|
@lock.synchronize do
|
82
|
-
raise 'Count is too large.' if count > @size
|
81
|
+
raise Workers::PoolSizeError, 'Count is too large.' if count > @size
|
83
82
|
|
84
83
|
count.times do
|
85
84
|
callback = Proc.new do |worker|
|
@@ -92,7 +91,7 @@ module Workers
|
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
|
94
|
+
nil
|
96
95
|
end
|
97
96
|
|
98
97
|
def resize(new_size)
|
@@ -104,7 +103,7 @@ module Workers
|
|
104
103
|
end
|
105
104
|
end
|
106
105
|
|
107
|
-
|
106
|
+
nil
|
108
107
|
end
|
109
108
|
|
110
109
|
private
|
@@ -114,7 +113,7 @@ module Workers
|
|
114
113
|
@workers.delete(worker)
|
115
114
|
end
|
116
115
|
|
117
|
-
|
116
|
+
nil
|
118
117
|
end
|
119
118
|
end
|
120
119
|
end
|
data/lib/workers/scheduler.rb
CHANGED
@@ -9,7 +9,7 @@ module Workers
|
|
9
9
|
@mutex = Mutex.new
|
10
10
|
@thread = Thread.new { start_loop }
|
11
11
|
|
12
|
-
|
12
|
+
nil
|
13
13
|
end
|
14
14
|
|
15
15
|
def schedule(timer)
|
@@ -19,7 +19,7 @@ module Workers
|
|
19
19
|
|
20
20
|
wakeup
|
21
21
|
|
22
|
-
|
22
|
+
nil
|
23
23
|
end
|
24
24
|
|
25
25
|
def unschedule(timer)
|
@@ -27,13 +27,13 @@ module Workers
|
|
27
27
|
@schedule.delete(timer)
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
nil
|
31
31
|
end
|
32
32
|
|
33
33
|
def wakeup
|
34
34
|
@thread.wakeup
|
35
35
|
|
36
|
-
|
36
|
+
nil
|
37
37
|
end
|
38
38
|
|
39
39
|
def dispose
|
@@ -43,11 +43,11 @@ module Workers
|
|
43
43
|
@thread.kill
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
nil
|
47
47
|
end
|
48
48
|
|
49
49
|
def alive?
|
50
|
-
|
50
|
+
@thread && @thread.alive?
|
51
51
|
end
|
52
52
|
|
53
53
|
private
|
@@ -64,7 +64,7 @@ module Workers
|
|
64
64
|
delay ? sleep(delay) : sleep
|
65
65
|
end
|
66
66
|
|
67
|
-
|
67
|
+
nil
|
68
68
|
end
|
69
69
|
|
70
70
|
def process_overdue
|
@@ -84,7 +84,7 @@ module Workers
|
|
84
84
|
@schedule << timer if timer.repeat
|
85
85
|
end
|
86
86
|
|
87
|
-
|
87
|
+
nil
|
88
88
|
end
|
89
89
|
|
90
90
|
def next_delay
|
data/lib/workers/task.rb
CHANGED
@@ -12,19 +12,19 @@ module Workers
|
|
12
12
|
def initialize(options = {})
|
13
13
|
@logger = Workers::LogProxy.new(options[:logger])
|
14
14
|
@input = options[:input] || []
|
15
|
-
@perform = options[:perform] || raise('Perform callback is required.')
|
15
|
+
@perform = options[:perform] || raise(Workers::MissingCallbackError, 'Perform callback is required.')
|
16
16
|
@finished = options[:finished]
|
17
17
|
@max_tries = options[:max_tries] || 1
|
18
18
|
@state = :initialized
|
19
19
|
@tries = 0
|
20
20
|
|
21
|
-
raise 'max_tries must be >= 1' unless @max_tries >= 1
|
21
|
+
raise Workers::MaxTriesError, 'max_tries must be >= 1' unless @max_tries >= 1
|
22
22
|
|
23
|
-
|
23
|
+
nil
|
24
24
|
end
|
25
25
|
|
26
26
|
def run
|
27
|
-
raise "Invalid state (#{@state})." unless @state == :initialized
|
27
|
+
raise Workers::InvalidStateError, "Invalid state (#{@state})." unless @state == :initialized
|
28
28
|
|
29
29
|
@state = :running
|
30
30
|
|
@@ -43,15 +43,15 @@ module Workers
|
|
43
43
|
|
44
44
|
@finished.call(self)
|
45
45
|
|
46
|
-
|
46
|
+
nil
|
47
47
|
end
|
48
48
|
|
49
49
|
def succeeded?
|
50
|
-
|
50
|
+
@state == :succeeded
|
51
51
|
end
|
52
52
|
|
53
53
|
def failed?
|
54
|
-
|
54
|
+
@state == :failed
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
data/lib/workers/task_group.rb
CHANGED
@@ -15,7 +15,7 @@ module Workers
|
|
15
15
|
@finished_count = 0
|
16
16
|
@conditional = ConditionVariable.new
|
17
17
|
|
18
|
-
|
18
|
+
nil
|
19
19
|
end
|
20
20
|
|
21
21
|
def add(options = {}, &block)
|
@@ -26,7 +26,7 @@ module Workers
|
|
26
26
|
|
27
27
|
@tasks << Workers::Task.new(options)
|
28
28
|
|
29
|
-
|
29
|
+
nil
|
30
30
|
end
|
31
31
|
|
32
32
|
def run
|
@@ -45,15 +45,15 @@ module Workers
|
|
45
45
|
@conditional.wait(@internal_lock)
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
@tasks.all? { |t| t.succeeded? }
|
49
49
|
end
|
50
50
|
|
51
51
|
def successes
|
52
|
-
|
52
|
+
@tasks.select { |t| t.succeeded? }
|
53
53
|
end
|
54
54
|
|
55
55
|
def failures
|
56
|
-
|
56
|
+
@tasks.select { |t| t.failed? }
|
57
57
|
end
|
58
58
|
|
59
59
|
def map(inputs, options = {}, &block)
|
@@ -70,10 +70,10 @@ module Workers
|
|
70
70
|
m = failure.exception.message
|
71
71
|
b = failure.exception.backtrace.join("\n")
|
72
72
|
|
73
|
-
raise "At least one task failed. ARGS=#{a}, TRACE=#{m}\n#{b}\n----------\n"
|
73
|
+
raise Workers::FailedTaskError, "At least one task failed. ARGS=#{a}, TRACE=#{m}\n#{b}\n----------\n"
|
74
74
|
end
|
75
75
|
|
76
|
-
|
76
|
+
tasks.map { |t| t.result }
|
77
77
|
end
|
78
78
|
|
79
79
|
# Convenient mutex to be used by a users's task code that needs serializing.
|
@@ -81,17 +81,17 @@ module Workers
|
|
81
81
|
def synchronize(&block)
|
82
82
|
@external_lock.synchronize { block.call }
|
83
83
|
|
84
|
-
|
84
|
+
nil
|
85
85
|
end
|
86
86
|
|
87
87
|
private
|
88
88
|
|
89
89
|
def state!(*args)
|
90
90
|
unless args.include?(@state)
|
91
|
-
raise "Invalid state (#{@state})."
|
91
|
+
raise Workers::InvalidStateError, "Invalid state (#{@state})."
|
92
92
|
end
|
93
93
|
|
94
|
-
|
94
|
+
nil
|
95
95
|
end
|
96
96
|
|
97
97
|
def finished(task)
|
@@ -100,7 +100,7 @@ module Workers
|
|
100
100
|
@conditional.signal if @finished_count >= @tasks.count
|
101
101
|
end
|
102
102
|
|
103
|
-
|
103
|
+
nil
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
data/lib/workers/timer.rb
CHANGED
@@ -16,23 +16,22 @@ module Workers
|
|
16
16
|
reset
|
17
17
|
@scheduler.schedule(self)
|
18
18
|
|
19
|
-
|
19
|
+
nil
|
20
20
|
end
|
21
21
|
|
22
22
|
def <=>(other)
|
23
|
-
|
23
|
+
sec_remaining <=> other.sec_remaining
|
24
24
|
end
|
25
25
|
|
26
26
|
def sec_remaining
|
27
27
|
@mutex.synchronize do
|
28
28
|
diff = @fire_at.to_f - Time.now.utc.to_f
|
29
|
-
|
30
|
-
return (diff > 0) ? diff : 0
|
29
|
+
(diff > 0) ? diff : 0
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
33
|
def overdue?
|
35
|
-
|
34
|
+
sec_remaining <= 0
|
36
35
|
end
|
37
36
|
|
38
37
|
def fire
|
@@ -40,13 +39,13 @@ module Workers
|
|
40
39
|
@callback.call if @callback
|
41
40
|
end
|
42
41
|
|
43
|
-
|
42
|
+
nil
|
44
43
|
end
|
45
44
|
|
46
45
|
def cancel
|
47
46
|
@scheduler.unschedule(self)
|
48
47
|
|
49
|
-
|
48
|
+
nil
|
50
49
|
end
|
51
50
|
|
52
51
|
def reset
|
@@ -54,7 +53,7 @@ module Workers
|
|
54
53
|
@fire_at = Time.now.utc + @delay
|
55
54
|
end
|
56
55
|
|
57
|
-
|
56
|
+
nil
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
data/lib/workers/version.rb
CHANGED
data/lib/workers/worker.rb
CHANGED
@@ -2,82 +2,106 @@ module Workers
|
|
2
2
|
class Worker
|
3
3
|
include Workers::Helpers
|
4
4
|
|
5
|
+
attr_accessor :exception
|
6
|
+
|
5
7
|
def initialize(options = {})
|
6
8
|
@logger = Workers::LogProxy.new(options[:logger])
|
7
9
|
@input_queue = options[:input_queue] || Queue.new
|
8
10
|
@thread = Thread.new { start_event_loop }
|
11
|
+
@exception_callback = options[:on_exception]
|
12
|
+
@die_on_exception = options.include?(:die_on_exception) ? options[:die_on_exception] : true
|
9
13
|
|
10
|
-
|
14
|
+
nil
|
11
15
|
end
|
12
16
|
|
13
17
|
def enqueue(command, data = nil)
|
14
18
|
@input_queue.push(Event.new(command, data))
|
15
19
|
|
16
|
-
|
20
|
+
nil
|
17
21
|
end
|
18
22
|
|
19
23
|
def perform(&block)
|
20
24
|
enqueue(:perform, block)
|
21
25
|
|
22
|
-
|
26
|
+
nil
|
23
27
|
end
|
24
28
|
|
25
29
|
def shutdown(&block)
|
26
30
|
enqueue(:shutdown, block)
|
27
31
|
|
28
|
-
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def kill
|
36
|
+
@thread.kill
|
37
|
+
|
38
|
+
nil
|
29
39
|
end
|
30
40
|
|
31
41
|
def join(max_wait = nil)
|
32
|
-
raise "Worker can't join itself." if @thread == Thread.current
|
42
|
+
raise Workers::JoinError, "Worker can't join itself." if @thread == Thread.current
|
33
43
|
|
34
44
|
return true if !@thread.join(max_wait).nil?
|
35
45
|
|
36
46
|
@thread.kill and return false
|
37
47
|
end
|
38
48
|
|
49
|
+
def dispose(max_wait = nil)
|
50
|
+
shutdown
|
51
|
+
join(max_wait)
|
52
|
+
end
|
53
|
+
|
39
54
|
def alive?
|
40
|
-
|
55
|
+
@thread && @thread.alive?
|
41
56
|
end
|
42
57
|
|
43
58
|
def inspect
|
44
|
-
|
59
|
+
"#<#{self.class.to_s}:0x#{(object_id << 1).to_s(16)} #{alive? ? 'alive' : 'dead'}>"
|
45
60
|
end
|
46
61
|
|
47
62
|
private
|
48
63
|
|
49
64
|
def start_event_loop
|
50
|
-
while
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
while process_event; end
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_event
|
69
|
+
event = @input_queue.pop
|
70
|
+
|
71
|
+
case event.command
|
72
|
+
when :shutdown
|
73
|
+
shutdown_handler(event)
|
74
|
+
return false
|
75
|
+
when :perform
|
76
|
+
perform_handler(event)
|
77
|
+
else
|
78
|
+
raise Workers::UnknownEventError, "Unhandled event (#{event.inspect})."
|
62
79
|
end
|
80
|
+
|
81
|
+
true
|
63
82
|
rescue Exception => e
|
64
83
|
exception_handler(e)
|
84
|
+
true
|
65
85
|
end
|
66
86
|
|
67
87
|
def shutdown_handler(event)
|
68
88
|
event.data.call(self) if event.data
|
89
|
+
|
90
|
+
nil
|
69
91
|
end
|
70
92
|
|
71
93
|
def perform_handler(event)
|
72
|
-
event.data.call
|
94
|
+
event.data.call
|
95
|
+
|
96
|
+
nil
|
73
97
|
end
|
74
98
|
|
75
99
|
def exception_handler(e)
|
76
|
-
|
77
|
-
|
100
|
+
@exception = e
|
101
|
+
@exception_callback.call(e) if @exception_callback
|
102
|
+
raise(e) if @die_on_exception
|
78
103
|
|
79
|
-
|
80
|
-
raise "Unhandled event (#{event.inspect}). Subclass and override if you need custom events."
|
104
|
+
nil
|
81
105
|
end
|
82
106
|
end
|
83
107
|
end
|
data/workers.gemspec
CHANGED
@@ -1,19 +1,24 @@
|
|
1
|
-
#
|
1
|
+
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
require 'workers/version'
|
5
5
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
gem.description = %q{Workers is a Ruby gem for performing work in background threads.}
|
12
|
-
gem.summary = %q{Workers is a Ruby gem for performing work in background threads. Design goals include high performance, low latency, simple API, customizability, and multi-layered architecture. It provides a number of simple to use classes that solve a wide range of concurrency problems.}
|
13
|
-
gem.homepage = 'https://github.com/chadrem/workers'
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "workers"
|
8
|
+
spec.version = Workers::VERSION
|
9
|
+
spec.authors = ["Chad Remesch"]
|
10
|
+
spec.email = ["chad@remesch.com"]
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
spec.summary = %q{A Ruby gem for performing work in background threads.}
|
13
|
+
spec.homepage = "https://github.com/chadrem/workers"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "minitest"
|
19
24
|
end
|
metadata
CHANGED
@@ -1,16 +1,58 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chad Remesch
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2015-07-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
14
56
|
email:
|
15
57
|
- chad@remesch.com
|
16
58
|
executables: []
|
@@ -18,13 +60,17 @@ extensions: []
|
|
18
60
|
extra_rdoc_files: []
|
19
61
|
files:
|
20
62
|
- ".gitignore"
|
63
|
+
- ".travis.yml"
|
21
64
|
- Gemfile
|
22
65
|
- LICENSE.txt
|
23
66
|
- README.md
|
24
67
|
- Rakefile
|
68
|
+
- bin/console
|
69
|
+
- bin/setup
|
25
70
|
- lib/workers.rb
|
26
71
|
- lib/workers/bucket_scheduler.rb
|
27
72
|
- lib/workers/event.rb
|
73
|
+
- lib/workers/exceptions.rb
|
28
74
|
- lib/workers/helpers.rb
|
29
75
|
- lib/workers/log_proxy.rb
|
30
76
|
- lib/workers/periodic_timer.rb
|
@@ -37,7 +83,8 @@ files:
|
|
37
83
|
- lib/workers/worker.rb
|
38
84
|
- workers.gemspec
|
39
85
|
homepage: https://github.com/chadrem/workers
|
40
|
-
licenses:
|
86
|
+
licenses:
|
87
|
+
- MIT
|
41
88
|
metadata: {}
|
42
89
|
post_install_message:
|
43
90
|
rdoc_options: []
|
@@ -58,8 +105,5 @@ rubyforge_project:
|
|
58
105
|
rubygems_version: 2.4.5
|
59
106
|
signing_key:
|
60
107
|
specification_version: 4
|
61
|
-
summary:
|
62
|
-
include high performance, low latency, simple API, customizability, and multi-layered
|
63
|
-
architecture. It provides a number of simple to use classes that solve a wide range
|
64
|
-
of concurrency problems.
|
108
|
+
summary: A Ruby gem for performing work in background threads.
|
65
109
|
test_files: []
|