workers 0.0.4 → 0.0.5
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/README.md +88 -14
- data/lib/workers/actor.rb +79 -0
- data/lib/workers/dedicated_actor.rb +9 -0
- data/lib/workers/mailbox.rb +26 -0
- data/lib/workers/pool.rb +8 -1
- data/lib/workers/registry.rb +45 -0
- data/lib/workers/scheduler.rb +1 -1
- data/lib/workers/version.rb +1 -1
- data/lib/workers/worker.rb +1 -1
- data/lib/workers.rb +31 -1
- metadata +5 -1
data/README.md
CHANGED
@@ -116,35 +116,109 @@ You can create additional or custom ones as necessary:
|
|
116
116
|
# Shutdown the scheduler.
|
117
117
|
scheduler.dispose
|
118
118
|
|
119
|
+
## Actors
|
120
|
+
|
121
|
+
Actors are light weight concurrent objects that use asynchronous message passing to communicate with each other.
|
122
|
+
They are event driven and use a worker pool in order to execute their event loop.
|
123
|
+
|
124
|
+
# Create your custom actor class.
|
125
|
+
class MyActor < Workers::Actor
|
126
|
+
private
|
127
|
+
def initialize(options = {})
|
128
|
+
super
|
129
|
+
end
|
130
|
+
|
131
|
+
def process_event(event)
|
132
|
+
case event.command
|
133
|
+
when :my_custom
|
134
|
+
my_custom_handler(event)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def my_custom_handler(event)
|
139
|
+
puts "Received a custom event (#{event.inspect})"
|
140
|
+
end
|
141
|
+
|
142
|
+
def exception_handler(e)
|
143
|
+
puts concat_e("MyActor (#{identifier}) died.", e)
|
144
|
+
end
|
145
|
+
|
146
|
+
def shutdown_handler(event)
|
147
|
+
puts "MyActor (#{identifier}) is shutting down. Put cleanup code here."
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Create some named actors.
|
152
|
+
100.times do |i|
|
153
|
+
MyActor.new(:name => "my_actor_#{i}")
|
154
|
+
end
|
155
|
+
|
156
|
+
# Send an event to each actors. Find each actor using the registry.
|
157
|
+
100.times do |i|
|
158
|
+
actor = Workers.registry["my_actor_#{i}"]
|
159
|
+
actor.enqueue(:my_custom, 'hello world')
|
160
|
+
end
|
161
|
+
|
162
|
+
# Shutdown the actors.
|
163
|
+
100.times do |i|
|
164
|
+
actor = Workers.registry["my_actor_#{i}"]
|
165
|
+
actor.enqueue(:shutdown)
|
166
|
+
end
|
167
|
+
|
168
|
+
### Implementation notes
|
169
|
+
Because actors use a shared worker pool, it is important that they don't block for long periods of time.
|
170
|
+
If you need an actor that can block for long periods then you should give the actor a dedicated thread (:dedicated => true).
|
171
|
+
|
172
|
+
## Registries
|
173
|
+
|
174
|
+
Registries hold references to named actors.
|
175
|
+
In general you shouldn't have to create your own since there is a global one (Workers.registry).
|
176
|
+
|
119
177
|
## Options (defaults below):
|
120
178
|
|
121
179
|
pool = Workers::Pool.new(
|
122
|
-
:size => 20,
|
123
|
-
:logger => nil,
|
124
|
-
:worker_class => Workers::Worker
|
180
|
+
:size => 20, # Number of threads to create.
|
181
|
+
:logger => nil, # Ruby Logger instance.
|
182
|
+
:worker_class => Workers::Worker # Class of worker to use for this pool.
|
125
183
|
)
|
126
184
|
|
127
185
|
worker = Workers::Worker.new(
|
128
|
-
:logger => nil,
|
129
|
-
:input_queue => nil
|
186
|
+
:logger => nil, # Ruby Logger instance.
|
187
|
+
:input_queue => nil # Ruby Queue used for input events.
|
130
188
|
)
|
131
189
|
|
132
190
|
timer = Workers::Timer.new(1,
|
133
|
-
:logger => nil,
|
134
|
-
:repeat => false,
|
135
|
-
:scheduler => Workers.scheduler,
|
136
|
-
:callback => nil
|
191
|
+
:logger => nil, # Ruby logger instance.
|
192
|
+
:repeat => false, # Repeat the timer until 'cancel' is called.
|
193
|
+
:scheduler => Workers.scheduler, # The scheduler that manages execution.
|
194
|
+
:callback => nil # The proc to execute (provide this or a block, but not both).
|
137
195
|
)
|
138
196
|
|
139
197
|
timer = Workers::PeriodicTimer.new(1,
|
140
|
-
:logger => nil,
|
141
|
-
:scheduler => Workers.scheduler,
|
142
|
-
:callback => nil
|
198
|
+
:logger => nil, # Ruby logger instance.
|
199
|
+
:scheduler => Workers.scheduler, # The scheduler that manages execution.
|
200
|
+
:callback => nil # The proc to execute (provide this or a block, but not both).
|
143
201
|
)
|
144
202
|
|
145
203
|
scheduler = Workers::Scheduler.new(
|
146
|
-
:logger => nil,
|
147
|
-
:pool => Workers::Pool.new
|
204
|
+
:logger => nil, # Ruby logger instance.
|
205
|
+
:pool => Workers::Pool.new # The workers pool used to execute timer callbacks.
|
206
|
+
)
|
207
|
+
|
208
|
+
actor = Workers::Actor.new(
|
209
|
+
:logger => nil, # Ruby logger instance.
|
210
|
+
:dedicated => false, # If true, the actor runs with a worker pool that has one thread.
|
211
|
+
:pool => Workers.pool, # The workers pool used to execute events.
|
212
|
+
:mailbox => Workers::Mailbox.new, # The mailbox used to receive events.
|
213
|
+
:registry => Workers.registry, # The registry used to store a reference to the actor if it has a name.
|
214
|
+
:name => nil # The name of the actor (must be unique in the registry).
|
215
|
+
)
|
216
|
+
|
217
|
+
actor = Workers::DedicatedActor.new(
|
218
|
+
:logger => nil, # Ruby logger instance.
|
219
|
+
:mailbox => Workers::Mailbox.new, # The mailbox used to receive events.
|
220
|
+
:registry => Workers.registry, # The registry used to store a reference to the actor if it has a name.
|
221
|
+
:name => nil # The name of the actor (must be unique in the registry).
|
148
222
|
)
|
149
223
|
|
150
224
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Workers
|
2
|
+
class Actor
|
3
|
+
include Workers::Helpers
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@logger = Workers::LogProxy.new(options[:logger])
|
7
|
+
@dedicated = options[:dedicated] || false
|
8
|
+
@mailbox = options[:mailbox] || Workers::Mailbox.new
|
9
|
+
@registry = options[:registry] || Workers.registry
|
10
|
+
@name = options[:name]
|
11
|
+
@pool = @dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
|
12
|
+
@alive = true
|
13
|
+
|
14
|
+
@registry.register(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def enqueue(command, data = nil)
|
18
|
+
return false unless @alive
|
19
|
+
|
20
|
+
@mailbox.push(Event.new(command, data))
|
21
|
+
|
22
|
+
@pool.perform do
|
23
|
+
process_events
|
24
|
+
end
|
25
|
+
|
26
|
+
return true
|
27
|
+
end
|
28
|
+
|
29
|
+
def alive?
|
30
|
+
@mailbox.synchronize do
|
31
|
+
return @alive
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
return @name
|
37
|
+
end
|
38
|
+
|
39
|
+
def identifier
|
40
|
+
return @name ? "#{object_id}:#{@name}" : object_id
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def process_events
|
46
|
+
while (event = @mailbox.shift)
|
47
|
+
case event.command
|
48
|
+
when :shutdown
|
49
|
+
shutdown_handler(event)
|
50
|
+
@pool.shutdown if @dedicated
|
51
|
+
@mailbox.synchronize do
|
52
|
+
@alive = false
|
53
|
+
end
|
54
|
+
else
|
55
|
+
process_event(event)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue Exception => e
|
59
|
+
@alive = false
|
60
|
+
exception_handler(e)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Subclass and override the below methods.
|
65
|
+
#
|
66
|
+
|
67
|
+
def process_event(event)
|
68
|
+
puts "Actor (#{identifier}) received event (#{event.inspect})."
|
69
|
+
end
|
70
|
+
|
71
|
+
def exception_handler(e)
|
72
|
+
puts concat_e("Actor (#{identifier}) died.", e)
|
73
|
+
end
|
74
|
+
|
75
|
+
def shutdown_handler(event)
|
76
|
+
puts "Actor (#{identifier}) is shutting down."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Workers
|
2
|
+
class Mailbox
|
3
|
+
def initialize(options = {})
|
4
|
+
@messages = []
|
5
|
+
@mutex = Mutex.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def push(event)
|
9
|
+
@mutex.synchronize do
|
10
|
+
@messages.push(event)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def shift
|
15
|
+
@mutex.synchronize do
|
16
|
+
@messages.shift
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def synchronize(&block)
|
21
|
+
@mutex.synchronize do
|
22
|
+
block.call
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/workers/pool.rb
CHANGED
@@ -14,7 +14,7 @@ module Workers
|
|
14
14
|
@size.times { @workers << @worker_class.new(:input_queue => @input_queue) }
|
15
15
|
end
|
16
16
|
|
17
|
-
def enqueue(command, data)
|
17
|
+
def enqueue(command, data = nil)
|
18
18
|
@input_queue.push(Event.new(command, data))
|
19
19
|
|
20
20
|
return nil
|
@@ -35,5 +35,12 @@ module Workers
|
|
35
35
|
def join(max_wait = nil)
|
36
36
|
return @workers.map { |w| w.join(max_wait) }
|
37
37
|
end
|
38
|
+
|
39
|
+
def dispose
|
40
|
+
shutdown
|
41
|
+
join
|
42
|
+
|
43
|
+
return nil
|
44
|
+
end
|
38
45
|
end
|
39
46
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Workers
|
2
|
+
class Registry
|
3
|
+
def initialize
|
4
|
+
@mutex = Mutex.new
|
5
|
+
@actors_by_name = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def register(actor)
|
9
|
+
@mutex.synchronize do
|
10
|
+
return false unless actor.name
|
11
|
+
|
12
|
+
if @actors_by_name[actor.name]
|
13
|
+
raise "Actor already exists (#{actor.name})."
|
14
|
+
else
|
15
|
+
@actors_by_name[actor.name] = actor
|
16
|
+
end
|
17
|
+
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def unregister(actor)
|
23
|
+
@mutex.synchronize do
|
24
|
+
return false unless actor.name
|
25
|
+
return false unless @actors_by_name[actor.name]
|
26
|
+
|
27
|
+
@actors_by_name.delete(actor.name)
|
28
|
+
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](val)
|
34
|
+
@mutex.synchronize do
|
35
|
+
return @actors_by_name[val]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def dispose
|
40
|
+
@mutex.synchronize do
|
41
|
+
@actors_by_name.clear
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/workers/scheduler.rb
CHANGED
@@ -4,7 +4,7 @@ module Workers
|
|
4
4
|
|
5
5
|
def initialize(options = {})
|
6
6
|
@logger = Workers::LogProxy.new(options[:logger])
|
7
|
-
@pool = options[:pool] || Workers
|
7
|
+
@pool = options[:pool] || Workers.pool
|
8
8
|
@schedule = SortedSet.new
|
9
9
|
@mutex = Mutex.new
|
10
10
|
@thread = Thread.new { start_loop }
|
data/lib/workers/version.rb
CHANGED
data/lib/workers/worker.rb
CHANGED
data/lib/workers.rb
CHANGED
@@ -10,11 +10,41 @@ require 'workers/log_proxy'
|
|
10
10
|
require 'workers/scheduler'
|
11
11
|
require 'workers/timer'
|
12
12
|
require 'workers/periodic_timer'
|
13
|
+
require 'workers/mailbox'
|
14
|
+
require 'workers/actor'
|
15
|
+
require 'workers/dedicated_actor'
|
16
|
+
require 'workers/registry'
|
13
17
|
|
14
18
|
module Workers
|
19
|
+
def self.pool
|
20
|
+
return @pool ||= Workers::Pool.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.pool=(val)
|
24
|
+
@pool.dispose if @pool
|
25
|
+
@pool = val
|
26
|
+
end
|
27
|
+
|
15
28
|
def self.scheduler
|
16
29
|
return @scheduler ||= Workers::Scheduler.new
|
17
30
|
end
|
31
|
+
|
32
|
+
def self.scheduler=(val)
|
33
|
+
@scheduler.dispose if @scheduler
|
34
|
+
@scheduler = val
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.registry
|
38
|
+
return @registry ||= Workers::Registry.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.registry=(val)
|
42
|
+
@registry.dispose if @registry
|
43
|
+
@registry = val
|
44
|
+
end
|
18
45
|
end
|
19
46
|
|
20
|
-
|
47
|
+
# Force initialization of defaults.
|
48
|
+
Workers.pool
|
49
|
+
Workers.scheduler
|
50
|
+
Workers.registry
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -24,11 +24,15 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- Rakefile
|
26
26
|
- lib/workers.rb
|
27
|
+
- lib/workers/actor.rb
|
28
|
+
- lib/workers/dedicated_actor.rb
|
27
29
|
- lib/workers/event.rb
|
28
30
|
- lib/workers/helpers.rb
|
29
31
|
- lib/workers/log_proxy.rb
|
32
|
+
- lib/workers/mailbox.rb
|
30
33
|
- lib/workers/periodic_timer.rb
|
31
34
|
- lib/workers/pool.rb
|
35
|
+
- lib/workers/registry.rb
|
32
36
|
- lib/workers/scheduler.rb
|
33
37
|
- lib/workers/timer.rb
|
34
38
|
- lib/workers/version.rb
|