concurrent-ruby 0.3.0.pre.1 → 0.3.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -33
- data/lib/concurrent.rb +5 -11
- data/lib/concurrent/{channel.rb → actor.rb} +14 -18
- data/lib/concurrent/agent.rb +5 -4
- data/lib/concurrent/cached_thread_pool.rb +116 -25
- data/lib/concurrent/cached_thread_pool/worker.rb +91 -0
- data/lib/concurrent/event.rb +13 -14
- data/lib/concurrent/event_machine_defer_proxy.rb +0 -1
- data/lib/concurrent/executor.rb +0 -1
- data/lib/concurrent/fixed_thread_pool.rb +111 -14
- data/lib/concurrent/fixed_thread_pool/worker.rb +54 -0
- data/lib/concurrent/future.rb +0 -2
- data/lib/concurrent/global_thread_pool.rb +21 -3
- data/lib/concurrent/goroutine.rb +1 -5
- data/lib/concurrent/obligation.rb +0 -19
- data/lib/concurrent/promise.rb +2 -5
- data/lib/concurrent/runnable.rb +2 -8
- data/lib/concurrent/supervisor.rb +9 -4
- data/lib/concurrent/utilities.rb +24 -0
- data/lib/concurrent/version.rb +1 -1
- data/md/agent.md +3 -3
- data/md/future.md +4 -4
- data/md/promise.md +15 -25
- data/md/thread_pool.md +9 -8
- data/spec/concurrent/actor_spec.rb +377 -0
- data/spec/concurrent/agent_spec.rb +2 -1
- data/spec/concurrent/cached_thread_pool_spec.rb +19 -29
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +1 -1
- data/spec/concurrent/event_spec.rb +1 -1
- data/spec/concurrent/executor_spec.rb +0 -8
- data/spec/concurrent/fixed_thread_pool_spec.rb +27 -16
- data/spec/concurrent/future_spec.rb +0 -13
- data/spec/concurrent/global_thread_pool_spec.rb +73 -0
- data/spec/concurrent/goroutine_spec.rb +0 -15
- data/spec/concurrent/obligation_shared.rb +1 -38
- data/spec/concurrent/promise_spec.rb +28 -47
- data/spec/concurrent/supervisor_spec.rb +1 -2
- data/spec/concurrent/thread_pool_shared.rb +28 -7
- data/spec/concurrent/utilities_spec.rb +50 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/functions.rb +17 -0
- metadata +12 -27
- data/lib/concurrent/functions.rb +0 -105
- data/lib/concurrent/null_thread_pool.rb +0 -25
- data/lib/concurrent/thread_pool.rb +0 -149
- data/md/reactor.md +0 -32
- data/spec/concurrent/channel_spec.rb +0 -446
- data/spec/concurrent/functions_spec.rb +0 -197
- data/spec/concurrent/null_thread_pool_spec.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e3a37abe10cd8c068be7f5f34ec87ea665c4d8c
|
4
|
+
data.tar.gz: bbb6cf6902175de9995656c9ded8e611940edfb7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69f7670740cfb70d858462864993d38209050a435435cbd4a9202dda5146f3ab32629bc6e2206b016c2ec0d9a95b5ff1c7751a2ee8278ba6e6400fafb478d23f
|
7
|
+
data.tar.gz: 84ce8fdad2b1ccc72f93e6a5b08889937b9c27d2d4aea0560d191ac4e9338e5909fedbd1a9a128c228a36eb7f6fbcc6f3206130308658122d23d98e89a2a4239
|
data/README.md
CHANGED
@@ -55,12 +55,13 @@ Several features from Erlang, Go, Clojure, Java, and JavaScript have been implem
|
|
55
55
|
|
56
56
|
* Clojure inspired [Agent](https://github.com/jdantonio/concurrent-ruby/blob/master/md/agent.md)
|
57
57
|
* Clojure inspired [Future](https://github.com/jdantonio/concurrent-ruby/blob/master/md/future.md)
|
58
|
+
* Scala inspired [Actor](http://www.scala-lang.org/api/current/index.html#scala.actors.Actor)
|
58
59
|
* Go inspired [Goroutine](https://github.com/jdantonio/concurrent-ruby/blob/master/md/goroutine.md)
|
59
60
|
* JavaScript inspired [Promise](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md)
|
60
61
|
* Java inspired [Thread Pools](https://github.com/jdantonio/concurrent-ruby/blob/master/md/thread_pool.md)
|
61
62
|
* Old school [events](http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655(v=vs.85).aspx) from back in my Visual C++ days
|
62
63
|
* Scheduled task execution with the [Executor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/executor.md) service
|
63
|
-
* Erlang
|
64
|
+
* Erlang inspired [Supervisor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/supervisor.md) for managing long-running threads
|
64
65
|
|
65
66
|
### Is it any good?
|
66
67
|
|
@@ -68,11 +69,10 @@ Several features from Erlang, Go, Clojure, Java, and JavaScript have been implem
|
|
68
69
|
|
69
70
|
### Supported Ruby versions
|
70
71
|
|
71
|
-
MRI 1.9.2, 1.9.3,
|
72
|
-
fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
|
73
|
-
about
|
74
|
-
|
75
|
-
with any Ruby other than MRI.
|
72
|
+
MRI 1.9.2, 1.9.3, 2.0, 2.1, and JRuby (1.9 mode). This library is pure Ruby and has no gem dependencies.
|
73
|
+
It should be fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
|
74
|
+
about Rubinius or the others to fully support them. I can promise good karma and attribution on this page
|
75
|
+
to anyone wishing to take responsibility for verifying compaitibility with any Ruby other than MRI.
|
76
76
|
|
77
77
|
### Install
|
78
78
|
|
@@ -94,25 +94,6 @@ Once you've installed the gem you must `require` it in your project:
|
|
94
94
|
require 'concurrent'
|
95
95
|
```
|
96
96
|
|
97
|
-
### Kernel Methods
|
98
|
-
|
99
|
-
Many Ruby developers consider it bad form to add function to the global (Kernel) namespace.
|
100
|
-
I don't necessarily agree. If the function acts like a low-level feature of the language
|
101
|
-
I think it is OK to add the method to the `Kernel` module. To support my personal programming
|
102
|
-
style I have chosen to implement `Kernel` methods to instance many of the objects in this
|
103
|
-
library. Out of respect for the larger Ruby community I have made these methods optional.
|
104
|
-
They are not imported with the normal `require 'concurrent'` directive. To import these
|
105
|
-
functions you must import the `concurrent/functions` library.
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
require 'concurrent'
|
109
|
-
score = agent(10) #=> NoMethodError: undefined method `agent' for main:Object
|
110
|
-
|
111
|
-
require 'concurrent/functions'
|
112
|
-
score = agent(10) #=> #<Concurrent::Agent:0x35b2b28 ...
|
113
|
-
score.value #=> 10
|
114
|
-
```
|
115
|
-
|
116
97
|
### Examples
|
117
98
|
|
118
99
|
For complete examples, see the specific documentation linked above. Below are a few examples to whet your appetite.
|
@@ -139,9 +120,8 @@ sleep(0.5)
|
|
139
120
|
|
140
121
|
```ruby
|
141
122
|
require 'concurrent'
|
142
|
-
require 'concurrent/functions'
|
143
123
|
|
144
|
-
score =
|
124
|
+
score = Concurrent::Agent.new(10)
|
145
125
|
score.value #=> 10
|
146
126
|
|
147
127
|
score << proc{|current| current + 100 }
|
@@ -150,7 +130,7 @@ score.value #=> 110
|
|
150
130
|
|
151
131
|
score << proc{|current| current * 2 }
|
152
132
|
sleep(0.1)
|
153
|
-
|
133
|
+
score.value #=> 220
|
154
134
|
|
155
135
|
score << proc{|current| current - 50 }
|
156
136
|
sleep(0.1)
|
@@ -161,22 +141,19 @@ score.value #=> 170
|
|
161
141
|
|
162
142
|
```ruby
|
163
143
|
require 'concurrent'
|
164
|
-
require 'concurrent/functions'
|
165
144
|
|
166
|
-
count =
|
145
|
+
count = Concurrent::Future.new{ sleep(1); 10 }
|
167
146
|
count.state #=> :pending
|
168
147
|
# do stuff...
|
169
148
|
count.value #=> 10 (after blocking)
|
170
|
-
deref count #=> 10
|
171
149
|
```
|
172
150
|
|
173
151
|
#### Promise (JavaScript)
|
174
152
|
|
175
153
|
```ruby
|
176
154
|
require 'concurrent'
|
177
|
-
require 'concurrent/functions'
|
178
155
|
|
179
|
-
p =
|
156
|
+
p = Concurrent::Promise.new("Jerry", "D'Antonio"){|a, b| "#{a} #{b}" }.
|
180
157
|
then{|result| "Hello #{result}." }.
|
181
158
|
rescue(StandardError){|ex| puts "Boom!" }.
|
182
159
|
then{|result| "#{result} Would you like to play a game?"}
|
data/lib/concurrent.rb
CHANGED
@@ -1,14 +1,9 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
$ENABLE_BEHAVIOR_CHECK_ON_CONSTRUCTION ||= ( $DEGUG ? true : false )
|
4
|
-
require 'functional'
|
5
|
-
|
6
1
|
require 'concurrent/version'
|
7
2
|
|
8
|
-
require 'concurrent/event'
|
9
3
|
|
4
|
+
require 'concurrent/actor'
|
10
5
|
require 'concurrent/agent'
|
11
|
-
require 'concurrent/
|
6
|
+
require 'concurrent/event'
|
12
7
|
require 'concurrent/executor'
|
13
8
|
require 'concurrent/future'
|
14
9
|
require 'concurrent/goroutine'
|
@@ -16,12 +11,11 @@ require 'concurrent/obligation'
|
|
16
11
|
require 'concurrent/promise'
|
17
12
|
require 'concurrent/runnable'
|
18
13
|
require 'concurrent/supervisor'
|
14
|
+
require 'concurrent/utilities'
|
15
|
+
|
16
|
+
require 'concurrent/global_thread_pool'
|
19
17
|
|
20
|
-
require 'concurrent/thread_pool'
|
21
18
|
require 'concurrent/cached_thread_pool'
|
22
19
|
require 'concurrent/fixed_thread_pool'
|
23
|
-
require 'concurrent/null_thread_pool'
|
24
|
-
|
25
|
-
require 'concurrent/global_thread_pool'
|
26
20
|
|
27
21
|
require 'concurrent/event_machine_defer_proxy' if defined?(EventMachine)
|
@@ -5,15 +5,13 @@ require 'concurrent/runnable'
|
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
8
|
-
|
8
|
+
# http://www.scala-lang.org/api/current/index.html#scala.actors.Actor
|
9
|
+
class Actor
|
9
10
|
include Observable
|
10
11
|
include Runnable
|
11
|
-
behavior(:runnable)
|
12
12
|
|
13
|
-
def initialize
|
13
|
+
def initialize
|
14
14
|
@queue = Queue.new
|
15
|
-
@task = block
|
16
|
-
@errorback = errorback
|
17
15
|
end
|
18
16
|
|
19
17
|
def post(*message)
|
@@ -27,19 +25,23 @@ module Concurrent
|
|
27
25
|
return self
|
28
26
|
end
|
29
27
|
|
30
|
-
def self.pool(count,
|
28
|
+
def self.pool(count, &block)
|
31
29
|
raise ArgumentError.new('count must be greater than zero') unless count > 0
|
32
30
|
mailbox = Queue.new
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
actors = count.times.collect do
|
32
|
+
actor = self.new(&block)
|
33
|
+
actor.instance_variable_set(:@queue, mailbox)
|
34
|
+
actor
|
37
35
|
end
|
38
|
-
return Poolbox.new(mailbox),
|
36
|
+
return Poolbox.new(mailbox), actors
|
39
37
|
end
|
40
38
|
|
41
39
|
protected
|
42
40
|
|
41
|
+
def act(*args)
|
42
|
+
raise NotImplementedError.new("#{self.class} does not implement #act")
|
43
|
+
end
|
44
|
+
|
43
45
|
class Poolbox
|
44
46
|
|
45
47
|
def initialize(queue)
|
@@ -73,7 +75,7 @@ module Concurrent
|
|
73
75
|
message = @queue.pop
|
74
76
|
return if message == :stop
|
75
77
|
begin
|
76
|
-
result =
|
78
|
+
result = act(*message)
|
77
79
|
changed
|
78
80
|
notify_observers(Time.now, message, result)
|
79
81
|
rescue => ex
|
@@ -83,12 +85,6 @@ module Concurrent
|
|
83
85
|
|
84
86
|
# @private
|
85
87
|
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
88
|
end
|
93
89
|
end
|
94
90
|
end
|
data/lib/concurrent/agent.rb
CHANGED
@@ -2,6 +2,7 @@ require 'thread'
|
|
2
2
|
require 'observer'
|
3
3
|
|
4
4
|
require 'concurrent/global_thread_pool'
|
5
|
+
require 'concurrent/utilities'
|
5
6
|
|
6
7
|
module Concurrent
|
7
8
|
|
@@ -45,7 +46,7 @@ module Concurrent
|
|
45
46
|
alias_method :deref, :value
|
46
47
|
|
47
48
|
def rescue(clazz = nil, &block)
|
48
|
-
|
49
|
+
unless block.nil?
|
49
50
|
@mutex.synchronize do
|
50
51
|
@rescuers << Rescuer.new(clazz, block)
|
51
52
|
end
|
@@ -56,7 +57,7 @@ module Concurrent
|
|
56
57
|
alias_method :on_error, :rescue
|
57
58
|
|
58
59
|
def validate(&block)
|
59
|
-
@validator = block
|
60
|
+
@validator = block unless block.nil?
|
60
61
|
return self
|
61
62
|
end
|
62
63
|
alias_method :validates, :validate
|
@@ -64,7 +65,7 @@ module Concurrent
|
|
64
65
|
alias_method :validates_with, :validate
|
65
66
|
|
66
67
|
def post(&block)
|
67
|
-
Agent.thread_pool.post{ work(&block) }
|
68
|
+
Agent.thread_pool.post{ work(&block) } unless block.nil?
|
68
69
|
end
|
69
70
|
|
70
71
|
def <<(block)
|
@@ -93,7 +94,7 @@ module Concurrent
|
|
93
94
|
def work(&handler) # :nodoc:
|
94
95
|
begin
|
95
96
|
@mutex.synchronize do
|
96
|
-
result =
|
97
|
+
result = Concurrent::timeout(@timeout) do
|
97
98
|
handler.call(@value)
|
98
99
|
end
|
99
100
|
if @validator.nil? || @validator.call(result)
|
@@ -1,51 +1,142 @@
|
|
1
|
-
require '
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
require 'concurrent/event'
|
4
|
+
require 'concurrent/cached_thread_pool/worker'
|
2
5
|
|
3
6
|
module Concurrent
|
4
7
|
|
5
|
-
class CachedThreadPool
|
6
|
-
|
8
|
+
class CachedThreadPool
|
9
|
+
|
10
|
+
MIN_POOL_SIZE = 1
|
11
|
+
MAX_POOL_SIZE = 256
|
7
12
|
|
8
13
|
DEFAULT_THREAD_IDLETIME = 60
|
9
14
|
|
15
|
+
attr_accessor :max_threads
|
16
|
+
|
10
17
|
def initialize(opts = {})
|
11
18
|
@idletime = (opts[:idletime] || DEFAULT_THREAD_IDLETIME).to_i
|
12
|
-
|
19
|
+
raise ArgumentError.new('idletime must be greater than zero') if @idletime <= 0
|
20
|
+
|
21
|
+
@max_threads = opts[:max_threads] || opts[:max] || MAX_POOL_SIZE
|
22
|
+
if @max_threads < MIN_POOL_SIZE || @max_threads > MAX_POOL_SIZE
|
23
|
+
raise ArgumentError.new("size must be from #{MIN_POOL_SIZE} to #{MAX_POOL_SIZE}")
|
24
|
+
end
|
25
|
+
|
26
|
+
@state = :running
|
27
|
+
@pool = []
|
28
|
+
@terminator = Event.new
|
29
|
+
@mutex = Mutex.new
|
30
|
+
|
31
|
+
@busy = []
|
32
|
+
@idle = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def <<(block)
|
36
|
+
self.post(&block)
|
37
|
+
return self
|
13
38
|
end
|
14
39
|
|
15
40
|
def post(*args, &block)
|
16
|
-
raise ArgumentError.new('no block given')
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if
|
22
|
-
|
41
|
+
raise ArgumentError.new('no block given') if block.nil?
|
42
|
+
@mutex.synchronize do
|
43
|
+
break false unless @state == :running
|
44
|
+
|
45
|
+
if @idle.empty?
|
46
|
+
if @idle.length + @busy.length < @max_threads
|
47
|
+
worker = create_worker_thread
|
48
|
+
else
|
49
|
+
worker = @busy.shift
|
23
50
|
end
|
24
|
-
true
|
25
51
|
else
|
26
|
-
|
52
|
+
worker = @idle.pop
|
27
53
|
end
|
54
|
+
|
55
|
+
@busy.push(worker)
|
56
|
+
worker.signal(*args, &block)
|
57
|
+
|
58
|
+
prune_stale_workers
|
59
|
+
true
|
28
60
|
end
|
29
61
|
end
|
30
62
|
|
31
|
-
|
63
|
+
def running?
|
64
|
+
return @state == :running
|
65
|
+
end
|
66
|
+
|
67
|
+
def wait_for_termination(timeout = nil)
|
68
|
+
return @terminator.wait(timeout)
|
69
|
+
end
|
70
|
+
|
71
|
+
def shutdown
|
72
|
+
@mutex.synchronize do
|
73
|
+
break unless @state == :running
|
74
|
+
if @idle.empty? && @busy.empty?
|
75
|
+
@state = :shutdown
|
76
|
+
@terminator.set
|
77
|
+
else
|
78
|
+
@state = :shuttingdown
|
79
|
+
@idle.each{|worker| worker.stop }
|
80
|
+
@busy.each{|worker| worker.stop }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def kill
|
86
|
+
@mutex.synchronize do
|
87
|
+
break if @state == :shutdown
|
88
|
+
@state = :shutdown
|
89
|
+
@idle.each{|worker| worker.kill }
|
90
|
+
@busy.each{|worker| worker.kill }
|
91
|
+
@terminator.set
|
92
|
+
end
|
93
|
+
end
|
32
94
|
|
33
|
-
def
|
34
|
-
|
95
|
+
def length
|
96
|
+
@mutex.synchronize do
|
97
|
+
@state == :running ? @busy.length + @idle.length : 0
|
98
|
+
end
|
35
99
|
end
|
36
100
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
101
|
+
def on_worker_exit(worker)
|
102
|
+
@mutex.synchronize do
|
103
|
+
@idle.delete(worker)
|
104
|
+
@busy.delete(worker)
|
105
|
+
if @idle.empty? && @busy.empty? && @state != :running
|
106
|
+
@state = :shutdown
|
107
|
+
@terminator.set
|
108
|
+
end
|
43
109
|
end
|
44
110
|
end
|
45
111
|
|
46
|
-
def
|
47
|
-
@
|
48
|
-
|
112
|
+
def on_end_task(worker)
|
113
|
+
@mutex.synchronize do
|
114
|
+
break unless @state == :running
|
115
|
+
@busy.delete(worker)
|
116
|
+
@idle.push(worker)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def create_worker_thread
|
123
|
+
wrkr = Worker.new(self)
|
124
|
+
Thread.new(wrkr, self) do |worker, parent|
|
125
|
+
Thread.current.abort_on_exception = false
|
126
|
+
worker.run
|
127
|
+
parent.on_worker_exit(worker)
|
128
|
+
end
|
129
|
+
return wrkr
|
130
|
+
end
|
131
|
+
|
132
|
+
def prune_stale_workers
|
133
|
+
@idle.reject! do |worker|
|
134
|
+
if worker.idletime > @idletime
|
135
|
+
worker.stop
|
136
|
+
true
|
137
|
+
else
|
138
|
+
worker.dead?
|
139
|
+
end
|
49
140
|
end
|
50
141
|
end
|
51
142
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
class CachedThreadPool
|
6
|
+
|
7
|
+
class Worker
|
8
|
+
|
9
|
+
def initialize(parent)
|
10
|
+
@parent = parent
|
11
|
+
@mutex = Mutex.new
|
12
|
+
@idletime = Time.now
|
13
|
+
@resource = ConditionVariable.new
|
14
|
+
@tasks = Queue.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def idle?
|
18
|
+
return ! @idletime.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def dead?
|
22
|
+
return @mutex.synchronize do
|
23
|
+
@thread.nil? ? false : ! @thread.alive?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def idletime
|
28
|
+
return @mutex.synchronize do
|
29
|
+
@idletime.nil? ? 0 : Time.now.to_i - @idletime.to_i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def signal(*args, &block)
|
34
|
+
return @mutex.synchronize do
|
35
|
+
break(false) if @parent.nil?
|
36
|
+
@tasks << [args, block]
|
37
|
+
@resource.signal
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def stop
|
43
|
+
return @mutex.synchronize do
|
44
|
+
@tasks.clear
|
45
|
+
@tasks << :stop
|
46
|
+
@resource.signal
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def kill
|
51
|
+
@mutex.synchronize do
|
52
|
+
@idletime = Time.now
|
53
|
+
@parent = nil
|
54
|
+
Thread.kill(@thread) unless @thread.nil?
|
55
|
+
@thread = nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def run(thread = Thread.current)
|
60
|
+
@mutex.synchronize do
|
61
|
+
raise StandardError.new('already running') unless @thread.nil?
|
62
|
+
@thread = thread
|
63
|
+
end
|
64
|
+
|
65
|
+
loop do
|
66
|
+
task = @mutex.synchronize do
|
67
|
+
@resource.wait(@mutex, 60) if @tasks.empty?
|
68
|
+
|
69
|
+
@tasks.pop(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
if task == :stop
|
73
|
+
@thread = nil
|
74
|
+
@parent.on_worker_exit(self)
|
75
|
+
@parent = nil
|
76
|
+
break
|
77
|
+
end
|
78
|
+
|
79
|
+
#@parent.on_start_task(self)
|
80
|
+
begin
|
81
|
+
task.last.call(*task.first)
|
82
|
+
rescue
|
83
|
+
# let it fail
|
84
|
+
ensure
|
85
|
+
@parent.on_end_task(self)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|