concurrent-ruby 0.3.0.pre.1 → 0.3.0.pre.2
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 +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
|