workers 0.1.4 → 0.2.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/README.md CHANGED
@@ -19,11 +19,29 @@ Or install it yourself as:
19
19
 
20
20
  $ gem install workers
21
21
 
22
+ ## Parallel Map
23
+
24
+ Parallel map is the simplest way to get started with the Workers gem.
25
+ It is similar to Ruby's built in Array#map method except each element is mapped in parallel.
26
+
27
+ Workers.map([1, 2, 3, 4, 5]) { |i| i * i }
28
+
29
+ Any exceptions while mapping with cause the entire map method to fail.
30
+ If your block is prone to temporary failures (exceptions), you can retry it.
31
+
32
+ Workers.map([1, 2, 3, 4, 5], :max_tries => 100) do |i|
33
+ if rand <= 0.5
34
+ puts "Sometimes I like to fail while computing #{i} * #{i}."
35
+ raise 'sad face'
36
+ end
37
+
38
+ i * i
39
+ end
40
+
22
41
  ## Tasks
23
42
 
24
- Tasks and task groups build on top of pools of worker threads.
25
- They provide a means of parallelizing expensive computations, collecing results, and handling exceptions.
26
- These are the classes you normally work with in your application level code because they remove boiletplate.
43
+ Tasks and task groups provide more flexibility than parallel map.
44
+ The main benefit is that you get to decide how you want to handle exceptions.
27
45
 
28
46
  # Create a task group (it contains a pool of worker threads).
29
47
  group = Workers::TaskGroup.new
@@ -31,7 +49,7 @@ These are the classes you normally work with in your application level code beca
31
49
  # Add tasks to the group.
32
50
  10.times do |i|
33
51
  10.times do |j|
34
- group.add(i, j) do
52
+ group.add do
35
53
  group.synchronize { puts "Computing #{i} * #{j}..." }
36
54
  i * j # Last statement executed is the result of the task.
37
55
  end
@@ -63,25 +81,26 @@ These are the classes you normally work with in your application level code beca
63
81
  Note that instances of TaskGroup provide a 'synchronize' method.
64
82
  This method uses a mutex so you can serialize portions of your tasks that aren't thread safe.
65
83
 
66
- ## Parallel Map
67
-
68
- Parallel Map is syntactic sugar for tasks and task groups:
69
-
70
- group = Workers::TaskGroup.new
71
- group.map([1,2,3,4,5]) { |i| i * i }
84
+ #### Options
72
85
 
73
- The above syntax is equivalent to the below:
86
+ group = Workers::TaskGroup.new(
87
+ :logger => nil, # Ruby logger instance.
88
+ :pool => Workers.pool # The workers pool used to execute timer callbacks.
89
+ )
74
90
 
75
- Workers.map([1, 2, 3, 4, 5]) { |i| i * i }
91
+ task = Workers::Task.new(
92
+ :logger => nil, # Ruby logger instance.
93
+ :perform => proc {}, # Required option. Block of code to run.
94
+ :args => [], # Array of arguments passed to the 'perform' block.
95
+ :finished => nil, # Callback to execute after attempting to run the task.
96
+ :max_tries => 1, # Number of times to try completing the task (without an exception).
97
+ )
76
98
 
77
- Note that any task failures (exceptions) will cause an exception to be raised.
78
- This means that parallel map is "all or nothing" where as tasks and task groups leave error processing up to you.
99
+ ## Workers
79
100
 
80
- ## Workers - Basic
101
+ #### Basic
81
102
 
82
- There are many cases where tasks, task groups, and parallel map are too high-level.
83
- Fortunately you can use the lower level Worker class to solve these problems.
84
- The main purpose of the Worker class is to add an event system on top of the Thread class.
103
+ The main purpose of the Worker class is to add an event system on top of Ruby's built-in Thread class.
85
104
  This greatly simplifies inter-thread communication.
86
105
 
87
106
  # Initialize a worker pool.
@@ -103,7 +122,7 @@ This greatly simplifies inter-thread communication.
103
122
  # Wait for the workers to shutdown.
104
123
  pool.join
105
124
 
106
- ## Workers - Advanced
125
+ #### Advanced
107
126
 
108
127
  The Worker class is designed to be customized through inheritence and its event system:
109
128
 
@@ -138,6 +157,13 @@ The Worker class is designed to be customized through inheritence and its event
138
157
  Note that you can use custom workers without a pool.
139
158
  This effectively gives you direct access to a single event-driven thread.
140
159
 
160
+ #### Options
161
+
162
+ worker = Workers::Worker.new(
163
+ :logger => nil, # Ruby Logger instance.
164
+ :input_queue => nil # Ruby Queue used for input events.
165
+ )
166
+
141
167
  ## Pools
142
168
 
143
169
  As shown above, pools effectively allow a group of workers to share a work queue.
@@ -158,6 +184,14 @@ They have a few additional methods described below:
158
184
  # Resize the pool size to a specific value.
159
185
  pool.resize(20)
160
186
 
187
+ #### Options
188
+
189
+ pool = Workers::Pool.new(
190
+ :size => 20, # Number of threads to create.
191
+ :logger => nil, # Ruby Logger instance.
192
+ :worker_class => Workers::Worker # Class of worker to use for this pool.
193
+ )
194
+
161
195
  ## Timers
162
196
 
163
197
  Timers provide a way to execute code in the future:
@@ -178,6 +212,21 @@ Timers provide a way to execute code in the future:
178
212
  # Shutdown the timer.
179
213
  timer.cancel
180
214
 
215
+ #### Options
216
+
217
+ timer = Workers::Timer.new(1,
218
+ :logger => nil, # Ruby logger instance.
219
+ :repeat => false, # Repeat the timer until 'cancel' is called.
220
+ :scheduler => Workers.scheduler, # The scheduler that manages execution.
221
+ :callback => nil # The proc to execute (provide this or a block, but not both).
222
+ )
223
+
224
+ timer = Workers::PeriodicTimer.new(1,
225
+ :logger => nil, # Ruby logger instance.
226
+ :scheduler => Workers.scheduler, # The scheduler that manages execution.
227
+ :callback => nil # The proc to execute (provide this or a block, but not both).
228
+ )
229
+
181
230
  ## Schedulers
182
231
 
183
232
  Schedulers are what trigger Timers to fire.
@@ -201,43 +250,7 @@ You can create additional or custom ones as necessary:
201
250
  # Shutdown the scheduler.
202
251
  scheduler.dispose
203
252
 
204
- ## Options (defaults below):
205
-
206
- pool = Workers::Pool.new(
207
- :size => 20, # Number of threads to create.
208
- :logger => nil, # Ruby Logger instance.
209
- :worker_class => Workers::Worker # Class of worker to use for this pool.
210
- )
211
-
212
- worker = Workers::Worker.new(
213
- :logger => nil, # Ruby Logger instance.
214
- :input_queue => nil # Ruby Queue used for input events.
215
- )
216
-
217
- timer = Workers::Timer.new(1,
218
- :logger => nil, # Ruby logger instance.
219
- :repeat => false, # Repeat the timer until 'cancel' is called.
220
- :scheduler => Workers.scheduler, # The scheduler that manages execution.
221
- :callback => nil # The proc to execute (provide this or a block, but not both).
222
- )
223
-
224
- group = Workers::TaskGroup.new(
225
- :logger => nil, # Ruby logger instance.
226
- :pool => Workers.pool # The workers pool used to execute timer callbacks.
227
- )
228
-
229
- task = Workers::Task.new(
230
- :logger => nil, # Ruby logger instance.
231
- :perform => proc {}, # Required option. Block of code to run.
232
- :args => [], # Array of arguments passed to the 'perform' block.
233
- :finished => nil, # Callback to execute after attempting to run the task.
234
- )
235
-
236
- timer = Workers::PeriodicTimer.new(1,
237
- :logger => nil, # Ruby logger instance.
238
- :scheduler => Workers.scheduler, # The scheduler that manages execution.
239
- :callback => nil # The proc to execute (provide this or a block, but not both).
240
- )
253
+ #### Options
241
254
 
242
255
  scheduler = Workers::Scheduler.new(
243
256
  :logger => nil, # Ruby logger instance.
data/lib/workers/task.rb CHANGED
@@ -12,7 +12,11 @@ module Workers
12
12
  @args = options[:args] || []
13
13
  @perform = options[:perform] || raise('Perform callback is required.')
14
14
  @finished = options[:finished]
15
+ @max_tries = options[:max_tries] || 1
15
16
  @state = :initialized
17
+ @tries = 0
18
+
19
+ raise 'max_tries must be >= 1' unless @max_tries >= 1
16
20
 
17
21
  return nil
18
22
  end
@@ -22,12 +26,17 @@ module Workers
22
26
 
23
27
  @state = :running
24
28
 
25
- begin
26
- @result = @perform.call(*@args)
27
- @state = :succeeded
28
- rescue Exception => e
29
- @state = :failed
30
- @exception = e
29
+ while(@tries < @max_tries && @state != :succeeded)
30
+ @tries += 1
31
+
32
+ begin
33
+ @result = @perform.call(*@args)
34
+ @state = :succeeded
35
+ @exception = nil
36
+ rescue Exception => e
37
+ @state = :failed
38
+ @exception = e
39
+ end
31
40
  end
32
41
 
33
42
  @finished.call(self)
@@ -18,14 +18,13 @@ module Workers
18
18
  return nil
19
19
  end
20
20
 
21
- def add(*args, &block)
21
+ def add(options = {}, &block)
22
22
  state!(:initialized)
23
23
 
24
- if args[0].is_a?(Workers::Task)
25
- @tasks << args[0]
26
- else
27
- @tasks << Workers::Task.new(:args => args, :perform => block, :finished => method(:finished))
28
- end
24
+ options[:finished] = method(:finished)
25
+ options[:perform] ||= block
26
+
27
+ @tasks << Workers::Task.new(options)
29
28
 
30
29
  return nil
31
30
  end
@@ -57,9 +56,9 @@ module Workers
57
56
  return @tasks.select { |t| t.failed? }
58
57
  end
59
58
 
60
- def map(inputs, &block)
59
+ def map(inputs, options = {}, &block)
61
60
  inputs.each do |input|
62
- add(input) do |i|
61
+ add(:args => input, :max_tries => options[:max_tries]) do |i|
63
62
  block.call(i)
64
63
  end
65
64
  end
@@ -1,3 +1,3 @@
1
1
  module Workers
2
- VERSION = '0.1.4'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/workers.rb CHANGED
@@ -33,9 +33,9 @@ module Workers
33
33
  @scheduler = val
34
34
  end
35
35
 
36
- def self.map(vals, &block)
37
- return Workers::TaskGroup.new.map(vals) do |v|
38
- block.call(v)
36
+ def self.map(inputs, options = {}, &block)
37
+ return Workers::TaskGroup.new.map(inputs, options) do |i|
38
+ block.call(i)
39
39
  end
40
40
  end
41
41
  end
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.1.4
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: