workers 0.0.2 → 0.0.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Workers
2
2
 
3
3
  Workers is a Ruby gem for performing work in background threads.
4
- Design goals include high performance, low latency, simple API, and customizability.
4
+ Design goals include high performance, low latency, simple API, customizability, and multi-layered architecture.
5
5
 
6
6
  ## Installation
7
7
 
@@ -21,7 +21,7 @@ Or install it yourself as:
21
21
 
22
22
  # Initialize a worker pool.
23
23
  pool = Workers::Pool.new
24
-
24
+
25
25
  # Perform some work in the background.
26
26
  100.times do
27
27
  pool.perform do
@@ -29,18 +29,18 @@ Or install it yourself as:
29
29
  puts "Hello world from thread #{Thread.current.object_id}"
30
30
  end
31
31
  end
32
-
32
+
33
33
  # Tell the workers to shutdown.
34
34
  pool.shutdown do
35
35
  puts "Worker thread #{Thread.current.object_id} is shutting down."
36
36
  end
37
-
38
- # Wait for the workers to finish.
37
+
38
+ # Wait for the workers to shutdown.
39
39
  pool.join
40
40
 
41
41
  ## Advanced Usage
42
42
 
43
- The Worker class is designed to be customized.
43
+ The Worker class is designed to be customized through inheritence and its event system:
44
44
 
45
45
  # Create a custom worker class that handles custom commands.
46
46
  class CustomWorker < Workers::Worker
@@ -53,32 +53,101 @@ The Worker class is designed to be customized.
53
53
  end
54
54
  end
55
55
  end
56
-
56
+
57
57
  # Create a pool that uses your custom worker class.
58
58
  pool = Workers::Pool.new(:worker_class => CustomWorker)
59
-
59
+
60
60
  # Tell the workers to do some work using custom events.
61
61
  100.times do |i|
62
62
  pool.enqueue(:my_custom, i)
63
63
  end
64
-
64
+
65
65
  # Tell the workers to shutdown.
66
66
  pool.shutdown do
67
67
  puts "Worker thread #{Thread.current.object_id} is shutting down."
68
68
  end
69
-
70
- # Wait for it to finish working and shutdown.
69
+
70
+ # Wait for the workers to shutdown.
71
71
  pool.join
72
+
73
+ ## Timers
74
+
75
+ Timers provide a way to execute code in the future:
76
+
77
+ # Create a one shot timer that executes in 1.5 seconds.
78
+ timer = Workers::Timer.new(1.5) do
79
+ puts 'Hello world'
80
+ end
72
81
 
73
- ## Pool Options
82
+ # Create a periodic timer that loops infinitely or until 'cancel' is called.
83
+ timer = Workers::PeriodicTimer.new(1) do
84
+ puts 'Hello world many times'
85
+ end
86
+ sleep 5
87
+ timer.cancel
88
+
89
+ Callbacks execute using a Workers::Pool in case they contain blocking operations.
74
90
 
75
- The pool class takes a few options (defaults below):
91
+ ## Options (defaults below):
76
92
 
77
93
  pool = Workers::Pool.new(
78
94
  :size => 20, # Number of threads to create.
79
- :logger => nil # Ruby Logger instance.
95
+ :logger => nil, # Ruby Logger instance.
80
96
  :worker_class => Workers::Worker # Class of worker to use for this pool.
97
+ )
98
+
99
+ worker = Workers::Worker.new(
100
+ :logger => nil, # Ruby Logger instance.
101
+ :input_queue => nil # Ruby Queue used for input events.
102
+ )
103
+
104
+ timer = Workers::Timer.new(1,
105
+ :logger => nil, # Ruby logger instance.
106
+ :repeat => false, # Repeat the timer until 'cancel' is called.
107
+ :scheduler => Workers.scheduler, # The scheduler that manages execution.
108
+ :callback => nil # The proc to execute (provide this or a block, but not both).
109
+ )
110
+
111
+ timer = Workers::PeriodicTimer.new(1,
112
+ :logger => nil, # Ruby logger instance.
113
+ :scheduler => Workers.scheduler, # The scheduler that manages execution.
114
+ :callback => nil # The proc to execute (provide this or a block, but not both).
115
+ )
81
116
 
117
+
118
+ ## TODO - not yet implemented features
119
+
120
+ ### Tasks
121
+
122
+ Tasks and task groups build on top of worker pools.
123
+ They provide a means of parallelizing expensive computations and collecing the results:
124
+
125
+ # Create a task group (it contains a pool of workers).
126
+ group = Workers::TaskGroup.new
127
+
128
+ # Add tasks to the group.
129
+ 100.times do |i|
130
+ group.add(i) do
131
+ i * i
132
+ end
133
+ end
134
+
135
+ # Execute the tasks (blocks until the tasks complete).
136
+ group.run
137
+
138
+ # Review the results.
139
+ group.tasks.each do |t|
140
+ t.succeeded? # True or false (false if an exception occurred).
141
+ t.args # Input arguments (the value of i in this example).
142
+ t.result # Output value (the result of i * i in this example).
143
+ t.exception # The exception if one exists.
144
+ end
145
+
146
+ TaskGroup and Task can then be used to build an easy to use parallel map.
147
+ Care will have to taken regarding global data and the thread safety of data structures:
148
+
149
+ Workers.map([1, 2, 3, 4]) { |i| i * i }
150
+
82
151
  ## Contributing
83
152
 
84
153
  1. Fork it
@@ -0,0 +1,9 @@
1
+ module Workers
2
+ class PeriodicTimer < Workers::Timer
3
+ def initialize(delay, options = {}, &block)
4
+ options[:repeat] = true
5
+
6
+ super(delay, options, &block)
7
+ end
8
+ end
9
+ end
data/lib/workers/pool.rb CHANGED
@@ -20,13 +20,13 @@ module Workers
20
20
  return nil
21
21
  end
22
22
 
23
- def perform(options = {}, &block)
23
+ def perform(&block)
24
24
  enqueue(:perform, block)
25
25
 
26
26
  return nil
27
27
  end
28
28
 
29
- def shutdown(options = {}, &block)
29
+ def shutdown(&block)
30
30
  @size.times { enqueue(:shutdown, block) }
31
31
 
32
32
  return nil
@@ -0,0 +1,85 @@
1
+ module Workers
2
+ class Scheduler
3
+ def initialize(options = {})
4
+ @pool = options[:pool] || Workers::Pool.new
5
+ @schedule = SortedSet.new
6
+ @mutex = Mutex.new
7
+ @thread = Thread.new { start_loop }
8
+ end
9
+
10
+ def schedule(timer)
11
+ @mutex.synchronize do
12
+ @schedule << timer
13
+ end
14
+
15
+ wakeup
16
+
17
+ return nil
18
+ end
19
+
20
+ def unschedule(timer)
21
+ @mutex.synchronize do
22
+ @schedule.delete(timer)
23
+ end
24
+
25
+ return true
26
+ end
27
+
28
+ def wakeup
29
+ @thread.wakeup
30
+
31
+ return nil
32
+ end
33
+
34
+ def dispose
35
+ @mutex.synchronize do
36
+ @pool.shutdown
37
+ @pool.join
38
+ @thread.kill
39
+ end
40
+
41
+ return nil
42
+ end
43
+
44
+ private
45
+
46
+ def start_loop
47
+ while true
48
+ delay = nil
49
+
50
+ @mutex.synchronize do
51
+ process_overdue
52
+ delay = next_delay
53
+ end
54
+
55
+ delay ? sleep(delay) : sleep
56
+ end
57
+
58
+ return nil
59
+ end
60
+
61
+ def process_overdue
62
+ overdue = []
63
+
64
+ while @schedule.first && @schedule.first.overdue?
65
+ overdue << @schedule.first
66
+ @schedule.delete(@schedule.first)
67
+ end
68
+
69
+ overdue.each do |timer|
70
+ @pool.perform do
71
+ timer.fire
72
+ end
73
+
74
+ timer.reset
75
+ @schedule << timer if timer.repeat
76
+ end
77
+
78
+ return nil
79
+ end
80
+
81
+ def next_delay
82
+ @schedule.first ? @schedule.first.sec_remaining : nil
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,58 @@
1
+ module Workers
2
+ class Timer
3
+ attr_reader :delay
4
+ attr_reader :repeat
5
+
6
+ def initialize(delay, options = {}, &block)
7
+ @delay = delay
8
+ @callback = options[:callback] || block
9
+ @repeat = options[:repeat] || false
10
+ @scheduler = options[:scheduler] || Workers.scheduler
11
+ @logger = options[:logger]
12
+
13
+ @mutex = Mutex.new
14
+
15
+ reset
16
+
17
+ @scheduler.schedule(self)
18
+ end
19
+
20
+ def <=>(other)
21
+ return sec_remaining <=> other.sec_remaining
22
+ end
23
+
24
+ def sec_remaining
25
+ @mutex.synchronize do
26
+ diff = @fire_at.to_f - Time.now.utc.to_f
27
+
28
+ return (diff > 0) ? diff : 0
29
+ end
30
+ end
31
+
32
+ def overdue?
33
+ return sec_remaining <= 0
34
+ end
35
+
36
+ def fire
37
+ @mutex.synchronize do
38
+ @callback.call if @callback
39
+ end
40
+
41
+ return nil
42
+ end
43
+
44
+ def cancel
45
+ @scheduler.unschedule(self)
46
+
47
+ return nil
48
+ end
49
+
50
+ def reset
51
+ @mutex.synchronize do
52
+ @fire_at = Time.now.utc + @delay
53
+ end
54
+
55
+ return nil
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,3 @@
1
1
  module Workers
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -15,13 +15,13 @@ module Workers
15
15
  return nil
16
16
  end
17
17
 
18
- def perform(options = {}, &block)
18
+ def perform(&block)
19
19
  enqueue(:perform, block)
20
20
 
21
21
  return nil
22
22
  end
23
23
 
24
- def shutdown(options = {}, &block)
24
+ def shutdown(&block)
25
25
  enqueue(:shutdown, block)
26
26
 
27
27
  return nil
data/lib/workers.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'thread'
2
+ require 'set'
2
3
 
3
4
  require 'workers/version'
4
5
  require 'workers/helpers'
@@ -6,3 +7,14 @@ require 'workers/worker'
6
7
  require 'workers/pool'
7
8
  require 'workers/event'
8
9
  require 'workers/log_proxy'
10
+ require 'workers/scheduler'
11
+ require 'workers/timer'
12
+ require 'workers/periodic_timer'
13
+
14
+ module Workers
15
+ def self.scheduler
16
+ return @scheduler ||= Workers::Scheduler.new
17
+ end
18
+ end
19
+
20
+ Workers.scheduler # Force initialization of default scheduler.
data/workers.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Workers::VERSION
9
9
  gem.authors = ["Chad Remesch"]
10
10
  gem.email = ["chad@remesch.com"]
11
- gem.description = %q{Simple to use Ruby workers for performing work in background threads.}
12
- gem.summary = %q{Simple to use Ruby workers featuring high performance, simple to use API, customizable workers, and thread pooling.}
11
+ gem.description = %q{Simple Ruby workers 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.}
13
13
  gem.homepage = "https://github.com/chadrem/workers"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
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.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,9 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-12 00:00:00.000000000 Z
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Simple to use Ruby workers for performing work in background threads.
14
+ description: Simple Ruby workers for performing work in background threads.
15
15
  email:
16
16
  - chad@remesch.com
17
17
  executables: []
@@ -27,7 +27,10 @@ files:
27
27
  - lib/workers/event.rb
28
28
  - lib/workers/helpers.rb
29
29
  - lib/workers/log_proxy.rb
30
+ - lib/workers/periodic_timer.rb
30
31
  - lib/workers/pool.rb
32
+ - lib/workers/scheduler.rb
33
+ - lib/workers/timer.rb
31
34
  - lib/workers/version.rb
32
35
  - lib/workers/worker.rb
33
36
  - workers.gemspec
@@ -54,6 +57,7 @@ rubyforge_project:
54
57
  rubygems_version: 1.8.24
55
58
  signing_key:
56
59
  specification_version: 3
57
- summary: Simple to use Ruby workers featuring high performance, simple to use API,
58
- customizable workers, and thread pooling.
60
+ summary: Workers is a Ruby gem for performing work in background threads. Design goals
61
+ include high performance, low latency, simple API, customizability, and multi-layered
62
+ architecture.
59
63
  test_files: []