concurrent-ruby 0.2.2 → 0.3.0.pre.1
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/README.md +45 -42
- data/lib/concurrent.rb +5 -6
- data/lib/concurrent/agent.rb +29 -33
- data/lib/concurrent/cached_thread_pool.rb +26 -105
- data/lib/concurrent/channel.rb +94 -0
- data/lib/concurrent/event.rb +8 -17
- data/lib/concurrent/executor.rb +68 -72
- data/lib/concurrent/fixed_thread_pool.rb +15 -83
- data/lib/concurrent/functions.rb +7 -22
- data/lib/concurrent/future.rb +29 -9
- data/lib/concurrent/null_thread_pool.rb +5 -2
- data/lib/concurrent/obligation.rb +6 -16
- data/lib/concurrent/promise.rb +9 -10
- data/lib/concurrent/runnable.rb +103 -0
- data/lib/concurrent/supervisor.rb +271 -44
- data/lib/concurrent/thread_pool.rb +112 -39
- data/lib/concurrent/version.rb +1 -1
- data/md/executor.md +9 -3
- data/md/goroutine.md +11 -9
- data/md/reactor.md +32 -0
- data/md/supervisor.md +43 -0
- data/spec/concurrent/agent_spec.rb +128 -51
- data/spec/concurrent/cached_thread_pool_spec.rb +33 -47
- data/spec/concurrent/channel_spec.rb +446 -0
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +3 -1
- data/spec/concurrent/event_spec.rb +0 -19
- data/spec/concurrent/executor_spec.rb +167 -119
- data/spec/concurrent/fixed_thread_pool_spec.rb +40 -30
- data/spec/concurrent/functions_spec.rb +0 -20
- data/spec/concurrent/future_spec.rb +88 -0
- data/spec/concurrent/null_thread_pool_spec.rb +23 -2
- data/spec/concurrent/obligation_shared.rb +0 -5
- data/spec/concurrent/promise_spec.rb +9 -10
- data/spec/concurrent/runnable_shared.rb +62 -0
- data/spec/concurrent/runnable_spec.rb +233 -0
- data/spec/concurrent/supervisor_spec.rb +912 -47
- data/spec/concurrent/thread_pool_shared.rb +18 -31
- data/spec/spec_helper.rb +10 -3
- metadata +17 -23
- data/lib/concurrent/defer.rb +0 -65
- data/lib/concurrent/reactor.rb +0 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +0 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +0 -131
- data/lib/concurrent/utilities.rb +0 -32
- data/md/defer.md +0 -174
- data/spec/concurrent/defer_spec.rb +0 -199
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +0 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +0 -410
- data/spec/concurrent/reactor_spec.rb +0 -364
- data/spec/concurrent/utilities_spec.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed299ebc57fa2caa62d5538e28f6506d175be4f0
|
4
|
+
data.tar.gz: 4e160d0efeefab15be0c1767679079f9d07b5b59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5687e0000024468f66f42cfd4742ff672502ed6f9cde7a9579a84acfdcdfde8af33b2df28a0b387f36767bfefe3d70411b8efa7a5ba8cb3f7a684112db5a11ec
|
7
|
+
data.tar.gz: d8e45ab66ecb79ad64310e8a31f604c146840d9abf5bd51e9dcbf48279767dd0c9a02fad2f37e5bef25f9ec5ca656d92bf87ccbcced9e2aa7ff9bc8e9e47fca5
|
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
# Concurrent Ruby [](https://travis-ci.org/jdantonio/concurrent-ruby?branch=master) [](https://gemnasium.com/jdantonio/concurrent-ruby)
|
1
|
+
# Concurrent Ruby [](https://travis-ci.org/jdantonio/concurrent-ruby?branch=master) [](https://coveralls.io/r/jdantonio/concurrent-ruby) [](https://gemnasium.com/jdantonio/concurrent-ruby)
|
2
2
|
|
3
|
-
Modern concurrency tools including agents, futures, promises, thread pools,
|
3
|
+
Modern concurrency tools including agents, futures, promises, thread pools, supervisors, and more.
|
4
4
|
Inspired by Erlang, Clojure, Go, JavaScript, actors, and classic concurrency patterns.
|
5
5
|
|
6
|
+
If you find this gem useful you should check out my [functional-ruby](https://github.com/jdantonio/functional-ruby)
|
7
|
+
gem, too. This gem uses several of the tools in that gem.
|
8
|
+
|
6
9
|
## Introduction
|
7
10
|
|
8
11
|
The old-school "lock and synchronize" approach to concurrency is dead. The future of concurrency
|
@@ -51,12 +54,13 @@ Specifically:
|
|
51
54
|
Several features from Erlang, Go, Clojure, Java, and JavaScript have been implemented thus far:
|
52
55
|
|
53
56
|
* Clojure inspired [Agent](https://github.com/jdantonio/concurrent-ruby/blob/master/md/agent.md)
|
54
|
-
* EventMachine inspired [Defer](https://github.com/jdantonio/concurrent-ruby/blob/master/md/defer.md)
|
55
57
|
* Clojure inspired [Future](https://github.com/jdantonio/concurrent-ruby/blob/master/md/future.md)
|
56
58
|
* Go inspired [Goroutine](https://github.com/jdantonio/concurrent-ruby/blob/master/md/goroutine.md)
|
57
59
|
* JavaScript inspired [Promise](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md)
|
58
60
|
* Java inspired [Thread Pools](https://github.com/jdantonio/concurrent-ruby/blob/master/md/thread_pool.md)
|
61
|
+
* Old school [events](http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655(v=vs.85).aspx) from back in my Visual C++ days
|
59
62
|
* Scheduled task execution with the [Executor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/executor.md) service
|
63
|
+
* Erlang-inspired [Supervisor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/supervisor.md) for managing long-running threads
|
60
64
|
|
61
65
|
### Is it any good?
|
62
66
|
|
@@ -118,10 +122,17 @@ For complete examples, see the specific documentation linked above. Below are a
|
|
118
122
|
```ruby
|
119
123
|
require 'concurrent'
|
120
124
|
|
121
|
-
|
122
|
-
go(
|
123
|
-
sleep(0.1)
|
124
|
-
|
125
|
+
go('foo'){|echo| sleep(0.1); print "#{echo}\n"; sleep(0.1); print "Boom!\n" }
|
126
|
+
go('bar'){|echo| sleep(0.1); print "#{echo}\n"; sleep(0.1); print "Pow!\n" }
|
127
|
+
go('baz'){|echo| sleep(0.1); print "#{echo}\n"; sleep(0.1); print "Zap!\n" }
|
128
|
+
sleep(0.5)
|
129
|
+
|
130
|
+
#=> foo
|
131
|
+
#=> bar
|
132
|
+
#=> baz
|
133
|
+
#=> Boom!
|
134
|
+
#=> Pow!
|
135
|
+
#=> Zap!
|
125
136
|
```
|
126
137
|
|
127
138
|
#### Agent (Clojure)
|
@@ -146,28 +157,6 @@ sleep(0.1)
|
|
146
157
|
score.value #=> 170
|
147
158
|
```
|
148
159
|
|
149
|
-
#### Defer (EventMachine)
|
150
|
-
|
151
|
-
```ruby
|
152
|
-
require 'concurrent'
|
153
|
-
require 'concurrent/functions'
|
154
|
-
|
155
|
-
Concurrent::Defer.new{ "Jerry D'Antonio" }.
|
156
|
-
then{|result| puts "Hello, #{result}!" }.
|
157
|
-
rescue{|ex| puts ex.message }.
|
158
|
-
go
|
159
|
-
|
160
|
-
#=> Hello, Jerry D'Antonio!
|
161
|
-
|
162
|
-
operation = proc{ raise StandardError.new('Boom!') }
|
163
|
-
callback = proc{|result| puts result }
|
164
|
-
errorback = proc{|ex| puts ex.message }
|
165
|
-
defer(operation, callback, errorback)
|
166
|
-
sleep(0.1)
|
167
|
-
|
168
|
-
#=> "Boom!"
|
169
|
-
```
|
170
|
-
|
171
160
|
#### Future (Clojure)
|
172
161
|
|
173
162
|
```ruby
|
@@ -200,23 +189,37 @@ p.value #=> "Hello Jerry D'Antonio. Would you like to play a game?"
|
|
200
189
|
```ruby
|
201
190
|
require 'concurrent'
|
202
191
|
|
203
|
-
pool = Concurrent::FixedThreadPool.new(
|
204
|
-
|
205
|
-
|
206
|
-
pool.post{ sleep(0.5);
|
207
|
-
pool.
|
208
|
-
|
192
|
+
pool = Concurrent::FixedThreadPool.new(2)
|
193
|
+
pool.size #=> 2
|
194
|
+
|
195
|
+
pool.post{ sleep(0.5); print "Boom!\n" }
|
196
|
+
pool.size #=> 2
|
197
|
+
pool.post{ sleep(0.5); print "Pow!\n" }
|
198
|
+
pool.size #=> 2
|
199
|
+
pool.post{ sleep(0.5); print "Zap!\n" }
|
200
|
+
pool.size #=> 2
|
201
|
+
|
209
202
|
sleep(1)
|
210
|
-
|
203
|
+
|
204
|
+
#=> Boom!
|
205
|
+
#=> Pow!
|
206
|
+
#=> Zap!
|
211
207
|
|
212
208
|
pool = Concurrent::CachedThreadPool.new
|
213
|
-
|
214
|
-
|
215
|
-
pool << proc{ sleep(0.5);
|
216
|
-
pool
|
217
|
-
|
209
|
+
pool.size #=> 0
|
210
|
+
|
211
|
+
pool << proc{ sleep(0.5); print "Boom!\n" }
|
212
|
+
pool.size #=> 1
|
213
|
+
pool << proc{ sleep(0.5); print "Pow!\n" }
|
214
|
+
pool.size #=> 2
|
215
|
+
pool << proc{ sleep(0.5); print "Zap!\n" }
|
216
|
+
pool.size #=> 3
|
217
|
+
|
218
218
|
sleep(1)
|
219
|
-
|
219
|
+
|
220
|
+
#=> Boom!
|
221
|
+
#=> Pow!
|
222
|
+
#=> Zap!
|
220
223
|
```
|
221
224
|
|
222
225
|
#### Executor
|
data/lib/concurrent.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
+
$ENABLE_BEHAVIOR_CHECK_ON_CONSTRUCTION ||= ( $DEGUG ? true : false )
|
4
|
+
require 'functional'
|
5
|
+
|
3
6
|
require 'concurrent/version'
|
4
7
|
|
5
8
|
require 'concurrent/event'
|
6
9
|
|
7
10
|
require 'concurrent/agent'
|
8
|
-
require 'concurrent/
|
11
|
+
require 'concurrent/channel'
|
9
12
|
require 'concurrent/executor'
|
10
13
|
require 'concurrent/future'
|
11
14
|
require 'concurrent/goroutine'
|
12
15
|
require 'concurrent/obligation'
|
13
16
|
require 'concurrent/promise'
|
17
|
+
require 'concurrent/runnable'
|
14
18
|
require 'concurrent/supervisor'
|
15
|
-
require 'concurrent/utilities'
|
16
|
-
|
17
|
-
require 'concurrent/reactor'
|
18
|
-
require 'concurrent/reactor/drb_async_demux'
|
19
|
-
require 'concurrent/reactor/tcp_sync_demux'
|
20
19
|
|
21
20
|
require 'concurrent/thread_pool'
|
22
21
|
require 'concurrent/cached_thread_pool'
|
data/lib/concurrent/agent.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require 'observer'
|
2
1
|
require 'thread'
|
2
|
+
require 'observer'
|
3
3
|
|
4
4
|
require 'concurrent/global_thread_pool'
|
5
|
-
require 'concurrent/utilities'
|
6
5
|
|
7
6
|
module Concurrent
|
8
7
|
|
@@ -21,21 +20,31 @@ module Concurrent
|
|
21
20
|
attr_reader :initial
|
22
21
|
attr_reader :timeout
|
23
22
|
|
24
|
-
def initialize(initial,
|
23
|
+
def initialize(initial, opts = {})
|
25
24
|
@value = initial
|
26
|
-
@timeout = timeout
|
27
25
|
@rescuers = []
|
28
26
|
@validator = nil
|
29
|
-
@queue = Queue.new
|
30
|
-
@mutex = Mutex.new
|
31
27
|
|
32
|
-
|
28
|
+
@timeout = opts[:timeout] || TIMEOUT
|
29
|
+
@dup_on_deref = opts[:dup_on_deref] || opts[:dup] || false
|
30
|
+
@freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze] || false
|
31
|
+
@copy_on_deref = opts[:copy_on_deref] || opts[:copy]
|
32
|
+
|
33
|
+
@mutex = Mutex.new
|
33
34
|
end
|
34
35
|
|
35
|
-
def value(timeout = 0)
|
36
|
+
def value(timeout = 0)
|
37
|
+
return @mutex.synchronize do
|
38
|
+
value = @value
|
39
|
+
value = @copy_on_deref.call(value) if @copy_on_deref
|
40
|
+
value = value.dup if @dup_on_deref
|
41
|
+
value = value.freeze if @freeze_on_deref
|
42
|
+
value
|
43
|
+
end
|
44
|
+
end
|
36
45
|
alias_method :deref, :value
|
37
46
|
|
38
|
-
def rescue(clazz =
|
47
|
+
def rescue(clazz = nil, &block)
|
39
48
|
if block_given?
|
40
49
|
@mutex.synchronize do
|
41
50
|
@rescuers << Rescuer.new(clazz, block)
|
@@ -55,11 +64,7 @@ module Concurrent
|
|
55
64
|
alias_method :validates_with, :validate
|
56
65
|
|
57
66
|
def post(&block)
|
58
|
-
|
59
|
-
return @mutex.synchronize do
|
60
|
-
@queue << block
|
61
|
-
@queue.length
|
62
|
-
end
|
67
|
+
Agent.thread_pool.post{ work(&block) } if block_given?
|
63
68
|
end
|
64
69
|
|
65
70
|
def <<(block)
|
@@ -67,12 +72,6 @@ module Concurrent
|
|
67
72
|
return self
|
68
73
|
end
|
69
74
|
|
70
|
-
def length
|
71
|
-
return @queue.length
|
72
|
-
end
|
73
|
-
alias_method :size, :length
|
74
|
-
alias_method :count, :length
|
75
|
-
|
76
75
|
alias_method :add_watch, :add_observer
|
77
76
|
|
78
77
|
private
|
@@ -83,31 +82,28 @@ module Concurrent
|
|
83
82
|
# @private
|
84
83
|
def try_rescue(ex) # :nodoc:
|
85
84
|
rescuer = @mutex.synchronize do
|
86
|
-
@rescuers.find{|r| ex.is_a?(r.clazz) }
|
85
|
+
@rescuers.find{|r| r.clazz.nil? || ex.is_a?(r.clazz) }
|
87
86
|
end
|
88
87
|
rescuer.block.call(ex) if rescuer
|
89
|
-
rescue Exception =>
|
88
|
+
rescue Exception => ex
|
90
89
|
# supress
|
91
90
|
end
|
92
91
|
|
93
92
|
# @private
|
94
|
-
def work # :nodoc:
|
95
|
-
|
96
|
-
|
97
|
-
begin
|
93
|
+
def work(&handler) # :nodoc:
|
94
|
+
begin
|
95
|
+
@mutex.synchronize do
|
98
96
|
result = Timeout.timeout(@timeout) do
|
99
97
|
handler.call(@value)
|
100
98
|
end
|
101
99
|
if @validator.nil? || @validator.call(result)
|
102
|
-
@
|
103
|
-
|
104
|
-
|
105
|
-
notify_observers(Time.now, @value)
|
106
|
-
end
|
100
|
+
@value = result
|
101
|
+
changed
|
102
|
+
notify_observers(Time.now, @value)
|
107
103
|
end
|
108
|
-
rescue Exception => ex
|
109
|
-
try_rescue(ex)
|
110
104
|
end
|
105
|
+
rescue Exception => ex
|
106
|
+
try_rescue(ex)
|
111
107
|
end
|
112
108
|
end
|
113
109
|
end
|
@@ -1,131 +1,52 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
1
|
require 'concurrent/thread_pool'
|
4
|
-
require 'concurrent/utilities'
|
5
|
-
|
6
|
-
require 'functional/utilities'
|
7
2
|
|
8
3
|
module Concurrent
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
5
|
+
class CachedThreadPool < AbstractThreadPool
|
6
|
+
behavior(:global_thread_pool)
|
13
7
|
|
14
|
-
class CachedThreadPool < ThreadPool
|
15
|
-
behavior(:thread_pool)
|
16
|
-
|
17
|
-
DEFAULT_GC_INTERVAL = 60
|
18
8
|
DEFAULT_THREAD_IDLETIME = 60
|
19
9
|
|
20
|
-
attr_reader :working
|
21
|
-
|
22
10
|
def initialize(opts = {})
|
23
|
-
@
|
24
|
-
|
25
|
-
super()
|
26
|
-
@working = 0
|
27
|
-
end
|
28
|
-
|
29
|
-
def kill
|
30
|
-
@status = :killed
|
31
|
-
mutex.synchronize do
|
32
|
-
@pool.each{|t| Thread.kill(t.thread) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def size
|
37
|
-
return @pool.length
|
11
|
+
@idletime = (opts[:idletime] || DEFAULT_THREAD_IDLETIME).to_i
|
12
|
+
super(opts)
|
38
13
|
end
|
39
14
|
|
40
15
|
def post(*args, &block)
|
41
16
|
raise ArgumentError.new('no block given') unless block_given?
|
42
|
-
|
43
|
-
|
44
|
-
mutex.synchronize do
|
45
|
-
if @working >= @pool.length
|
46
|
-
create_worker_thread
|
47
|
-
end
|
17
|
+
return @mutex.synchronize do
|
18
|
+
if @state == :running
|
48
19
|
@queue << [args, block]
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# @private
|
57
|
-
def status # :nodoc:
|
58
|
-
mutex.synchronize do
|
59
|
-
@pool.collect do |worker|
|
60
|
-
[
|
61
|
-
worker.status,
|
62
|
-
worker.status == :idle ? delta(worker.idletime, timestamp) : nil,
|
63
|
-
worker.thread.status
|
64
|
-
]
|
20
|
+
at_capacity = @pool.empty? || ! @queue.empty? || @working >= @pool.size
|
21
|
+
if at_capacity && @pool.length < @max_threads
|
22
|
+
@pool << create_worker_thread
|
23
|
+
end
|
24
|
+
true
|
25
|
+
else
|
26
|
+
false
|
65
27
|
end
|
66
28
|
end
|
67
29
|
end
|
68
30
|
|
69
|
-
|
70
|
-
|
71
|
-
Worker = Struct.new(:status, :idletime, :thread)
|
72
|
-
|
73
|
-
# @private
|
74
|
-
def create_worker_thread # :nodoc:
|
75
|
-
worker = Worker.new(:idle, timestamp, nil)
|
76
|
-
|
77
|
-
worker.thread = Thread.new(worker) do |me|
|
78
|
-
|
79
|
-
loop do
|
80
|
-
task = @queue.pop
|
31
|
+
protected
|
81
32
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
if task == :stop
|
88
|
-
me.status = :stopping
|
89
|
-
break
|
90
|
-
else
|
91
|
-
task.last.call(*task.first)
|
92
|
-
mutex.synchronize do
|
93
|
-
@working -= 1
|
94
|
-
me.status = :idle
|
95
|
-
me.idletime = timestamp
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
33
|
+
def dead_worker?(context)
|
34
|
+
return context.thread.nil? || context.thread.status == 'aborting' || ! context.thread.status
|
35
|
+
end
|
99
36
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
37
|
+
def stale_worker?(context)
|
38
|
+
if context.status == :idle && @idletime <= (timestamp - context.idletime)
|
39
|
+
context.thread.kill
|
40
|
+
return true
|
41
|
+
else
|
42
|
+
return false
|
107
43
|
end
|
108
|
-
|
109
|
-
worker.thread.abort_on_exception = false
|
110
|
-
@pool << worker
|
111
44
|
end
|
112
45
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
loop do
|
117
|
-
sleep(@gc_interval)
|
118
|
-
mutex.synchronize do
|
119
|
-
@pool.reject! do |worker|
|
120
|
-
worker.thread.status.nil? ||
|
121
|
-
(worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
|
122
|
-
end
|
123
|
-
end
|
124
|
-
@working = @pool.count{|worker| worker.status == :working}
|
125
|
-
break if @pool.empty?
|
126
|
-
end
|
46
|
+
def collect_garbage
|
47
|
+
@pool.reject! do |context|
|
48
|
+
dead_worker?(context) || stale_worker?(context)
|
127
49
|
end
|
128
|
-
@collector.abort_on_exception = false
|
129
50
|
end
|
130
51
|
end
|
131
52
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'observer'
|
3
|
+
|
4
|
+
require 'concurrent/runnable'
|
5
|
+
|
6
|
+
module Concurrent
|
7
|
+
|
8
|
+
class Channel
|
9
|
+
include Observable
|
10
|
+
include Runnable
|
11
|
+
behavior(:runnable)
|
12
|
+
|
13
|
+
def initialize(errorback = nil, &block)
|
14
|
+
@queue = Queue.new
|
15
|
+
@task = block
|
16
|
+
@errorback = errorback
|
17
|
+
end
|
18
|
+
|
19
|
+
def post(*message)
|
20
|
+
return false unless running?
|
21
|
+
@queue.push(message)
|
22
|
+
return @queue.length
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(message)
|
26
|
+
post(*message)
|
27
|
+
return self
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.pool(count, errorback = nil, &block)
|
31
|
+
raise ArgumentError.new('count must be greater than zero') unless count > 0
|
32
|
+
mailbox = Queue.new
|
33
|
+
channels = count.times.collect do
|
34
|
+
channel = self.new(errorback, &block)
|
35
|
+
channel.instance_variable_set(:@queue, mailbox)
|
36
|
+
channel
|
37
|
+
end
|
38
|
+
return Poolbox.new(mailbox), channels
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
class Poolbox
|
44
|
+
|
45
|
+
def initialize(queue)
|
46
|
+
@queue = queue
|
47
|
+
end
|
48
|
+
|
49
|
+
def post(*message)
|
50
|
+
@queue.push(message)
|
51
|
+
return @queue.length
|
52
|
+
end
|
53
|
+
|
54
|
+
def <<(message)
|
55
|
+
post(*message)
|
56
|
+
return self
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @private
|
61
|
+
def on_run # :nodoc:
|
62
|
+
@queue.clear
|
63
|
+
end
|
64
|
+
|
65
|
+
# @private
|
66
|
+
def on_stop # :nodoc:
|
67
|
+
@queue.clear
|
68
|
+
@queue.push(:stop)
|
69
|
+
end
|
70
|
+
|
71
|
+
# @private
|
72
|
+
def on_task # :nodoc:
|
73
|
+
message = @queue.pop
|
74
|
+
return if message == :stop
|
75
|
+
begin
|
76
|
+
result = receive(*message)
|
77
|
+
changed
|
78
|
+
notify_observers(Time.now, message, result)
|
79
|
+
rescue => ex
|
80
|
+
on_error(Time.now, message, ex)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# @private
|
85
|
+
def on_error(time, msg, ex) # :nodoc:
|
86
|
+
@errorback.call(time, msg, ex) if @errorback
|
87
|
+
end
|
88
|
+
|
89
|
+
# @private
|
90
|
+
def receive(*message) # :nodoc:
|
91
|
+
@task.call(*message) unless @task.nil?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|