workers 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: []