concurrent-ruby 0.2.0 → 0.2.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.
- data/LICENSE +21 -21
- data/README.md +275 -275
- data/lib/concurrent.rb +28 -28
- data/lib/concurrent/agent.rb +114 -114
- data/lib/concurrent/cached_thread_pool.rb +131 -129
- data/lib/concurrent/defer.rb +65 -65
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +96 -95
- data/lib/concurrent/fixed_thread_pool.rb +99 -95
- data/lib/concurrent/functions.rb +120 -120
- data/lib/concurrent/future.rb +42 -42
- data/lib/concurrent/global_thread_pool.rb +16 -16
- data/lib/concurrent/goroutine.rb +29 -29
- data/lib/concurrent/null_thread_pool.rb +22 -22
- data/lib/concurrent/obligation.rb +67 -67
- data/lib/concurrent/promise.rb +174 -174
- data/lib/concurrent/reactor.rb +166 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
- data/lib/concurrent/supervisor.rb +105 -100
- data/lib/concurrent/thread_pool.rb +76 -76
- data/lib/concurrent/utilities.rb +32 -32
- data/lib/concurrent/version.rb +3 -3
- data/lib/concurrent_ruby.rb +1 -1
- data/md/agent.md +123 -123
- data/md/defer.md +174 -174
- data/md/event.md +32 -32
- data/md/executor.md +187 -187
- data/md/future.md +83 -83
- data/md/goroutine.md +52 -52
- data/md/obligation.md +32 -32
- data/md/promise.md +227 -227
- data/md/thread_pool.md +224 -224
- data/spec/concurrent/agent_spec.rb +386 -386
- data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
- data/spec/concurrent/defer_spec.rb +195 -195
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +200 -200
- data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
- data/spec/concurrent/functions_spec.rb +217 -217
- data/spec/concurrent/future_spec.rb +108 -108
- data/spec/concurrent/global_thread_pool_spec.rb +38 -38
- data/spec/concurrent/goroutine_spec.rb +67 -67
- data/spec/concurrent/null_thread_pool_spec.rb +57 -54
- data/spec/concurrent/obligation_shared.rb +132 -132
- data/spec/concurrent/promise_spec.rb +312 -312
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
- data/spec/concurrent/reactor_spec.rb +364 -364
- data/spec/concurrent/supervisor_spec.rb +269 -258
- data/spec/concurrent/thread_pool_shared.rb +204 -204
- data/spec/concurrent/utilities_spec.rb +74 -74
- data/spec/spec_helper.rb +32 -32
- metadata +20 -16
- checksums.yaml +0 -7
@@ -1,95 +1,99 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/thread_pool'
|
4
|
-
require 'concurrent/event'
|
5
|
-
|
6
|
-
module Concurrent
|
7
|
-
|
8
|
-
def self.new_fixed_thread_pool(size)
|
9
|
-
return FixedThreadPool.new(size)
|
10
|
-
end
|
11
|
-
|
12
|
-
class FixedThreadPool < ThreadPool
|
13
|
-
behavior(:thread_pool)
|
14
|
-
|
15
|
-
MIN_POOL_SIZE = 1
|
16
|
-
MAX_POOL_SIZE = 1024
|
17
|
-
|
18
|
-
def initialize(size)
|
19
|
-
super()
|
20
|
-
if size < MIN_POOL_SIZE || size > MAX_POOL_SIZE
|
21
|
-
raise ArgumentError.new("size must be between #{MIN_POOL_SIZE} and #{MAX_POOL_SIZE}")
|
22
|
-
end
|
23
|
-
|
24
|
-
@pool = size.times.collect{ create_worker_thread }
|
25
|
-
collect_garbage
|
26
|
-
end
|
27
|
-
|
28
|
-
def kill
|
29
|
-
mutex.synchronize do
|
30
|
-
@status = :killed
|
31
|
-
@pool.each{|t| Thread.kill(t) }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def size
|
36
|
-
if running?
|
37
|
-
return @pool.length
|
38
|
-
else
|
39
|
-
return 0
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def post(*args, &block)
|
44
|
-
raise ArgumentError.new('no block given') unless block_given?
|
45
|
-
if running?
|
46
|
-
@queue << [args, block]
|
47
|
-
return true
|
48
|
-
else
|
49
|
-
return false
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# @private
|
54
|
-
def status # :nodoc:
|
55
|
-
mutex.synchronize do
|
56
|
-
@pool.collect{|t| t.status }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
# @private
|
63
|
-
def create_worker_thread # :nodoc:
|
64
|
-
Thread.new do
|
65
|
-
loop do
|
66
|
-
task = @queue.pop
|
67
|
-
if task == :stop
|
68
|
-
break
|
69
|
-
else
|
70
|
-
task.last.call(*task.first)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
@pool.delete(Thread.current)
|
74
|
-
if @pool.empty?
|
75
|
-
@termination.set
|
76
|
-
@status = :shutdown unless killed?
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
require 'concurrent/thread_pool'
|
4
|
+
require 'concurrent/event'
|
5
|
+
|
6
|
+
module Concurrent
|
7
|
+
|
8
|
+
def self.new_fixed_thread_pool(size)
|
9
|
+
return FixedThreadPool.new(size)
|
10
|
+
end
|
11
|
+
|
12
|
+
class FixedThreadPool < ThreadPool
|
13
|
+
behavior(:thread_pool)
|
14
|
+
|
15
|
+
MIN_POOL_SIZE = 1
|
16
|
+
MAX_POOL_SIZE = 1024
|
17
|
+
|
18
|
+
def initialize(size)
|
19
|
+
super()
|
20
|
+
if size < MIN_POOL_SIZE || size > MAX_POOL_SIZE
|
21
|
+
raise ArgumentError.new("size must be between #{MIN_POOL_SIZE} and #{MAX_POOL_SIZE}")
|
22
|
+
end
|
23
|
+
|
24
|
+
@pool = size.times.collect{ create_worker_thread }
|
25
|
+
collect_garbage
|
26
|
+
end
|
27
|
+
|
28
|
+
def kill
|
29
|
+
mutex.synchronize do
|
30
|
+
@status = :killed
|
31
|
+
@pool.each{|t| Thread.kill(t) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
if running?
|
37
|
+
return @pool.length
|
38
|
+
else
|
39
|
+
return 0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def post(*args, &block)
|
44
|
+
raise ArgumentError.new('no block given') unless block_given?
|
45
|
+
if running?
|
46
|
+
@queue << [args, block]
|
47
|
+
return true
|
48
|
+
else
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @private
|
54
|
+
def status # :nodoc:
|
55
|
+
mutex.synchronize do
|
56
|
+
@pool.collect{|t| t.status }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# @private
|
63
|
+
def create_worker_thread # :nodoc:
|
64
|
+
thread = Thread.new do
|
65
|
+
loop do
|
66
|
+
task = @queue.pop
|
67
|
+
if task == :stop
|
68
|
+
break
|
69
|
+
else
|
70
|
+
task.last.call(*task.first)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@pool.delete(Thread.current)
|
74
|
+
if @pool.empty?
|
75
|
+
@termination.set
|
76
|
+
@status = :shutdown unless killed?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
thread.abort_on_exception = false
|
81
|
+
return thread
|
82
|
+
end
|
83
|
+
|
84
|
+
# @private
|
85
|
+
def collect_garbage # :nodoc:
|
86
|
+
@collector = Thread.new do
|
87
|
+
sleep(1)
|
88
|
+
mutex.synchronize do
|
89
|
+
@pool.size.times do |i|
|
90
|
+
if @pool[i].status.nil?
|
91
|
+
@pool[i] = create_worker_thread
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
@collector.abort_on_exception = false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/concurrent/functions.rb
CHANGED
@@ -1,120 +1,120 @@
|
|
1
|
-
require 'concurrent/agent'
|
2
|
-
require 'concurrent/defer'
|
3
|
-
require 'concurrent/future'
|
4
|
-
require 'concurrent/promise'
|
5
|
-
|
6
|
-
module Kernel
|
7
|
-
|
8
|
-
## agent
|
9
|
-
|
10
|
-
def agent(initial, timeout = Concurrent::Agent::TIMEOUT)
|
11
|
-
return Concurrent::Agent.new(initial, timeout)
|
12
|
-
end
|
13
|
-
module_function :agent
|
14
|
-
|
15
|
-
def post(object, &block)
|
16
|
-
if object.respond_to?(:post)
|
17
|
-
return object.post(&block)
|
18
|
-
else
|
19
|
-
raise ArgumentError.new('object does not support #post')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
module_function :post
|
23
|
-
|
24
|
-
## defer
|
25
|
-
|
26
|
-
def defer(*args, &block)
|
27
|
-
return Concurrent::Defer.new(*args, &block)
|
28
|
-
end
|
29
|
-
module_function :defer
|
30
|
-
|
31
|
-
## executor
|
32
|
-
|
33
|
-
def executor(*args, &block)
|
34
|
-
return Concurrent::Executor.run(*args, &block)
|
35
|
-
end
|
36
|
-
module_function :executor
|
37
|
-
|
38
|
-
## future
|
39
|
-
|
40
|
-
def future(*args, &block)
|
41
|
-
return Concurrent::Future.new(*args, &block)
|
42
|
-
end
|
43
|
-
module_function :future
|
44
|
-
|
45
|
-
## obligation
|
46
|
-
|
47
|
-
def deref(object, timeout = nil)
|
48
|
-
if object.respond_to?(:deref)
|
49
|
-
return object.deref(timeout)
|
50
|
-
elsif object.respond_to?(:value)
|
51
|
-
return object.value(timeout)
|
52
|
-
else
|
53
|
-
raise ArgumentError.new('object does not support #deref')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
module_function :deref
|
57
|
-
|
58
|
-
def pending?(object)
|
59
|
-
if object.respond_to?(:pending?)
|
60
|
-
return object.pending?
|
61
|
-
else
|
62
|
-
raise ArgumentError.new('object does not support #pending?')
|
63
|
-
end
|
64
|
-
end
|
65
|
-
module_function :pending?
|
66
|
-
|
67
|
-
def fulfilled?(object)
|
68
|
-
if object.respond_to?(:fulfilled?)
|
69
|
-
return object.fulfilled?
|
70
|
-
elsif object.respond_to?(:realized?)
|
71
|
-
return object.realized?
|
72
|
-
else
|
73
|
-
raise ArgumentError.new('object does not support #fulfilled?')
|
74
|
-
end
|
75
|
-
end
|
76
|
-
module_function :fulfilled?
|
77
|
-
|
78
|
-
def realized?(object)
|
79
|
-
if object.respond_to?(:realized?)
|
80
|
-
return object.realized?
|
81
|
-
elsif object.respond_to?(:fulfilled?)
|
82
|
-
return object.fulfilled?
|
83
|
-
else
|
84
|
-
raise ArgumentError.new('object does not support #realized?')
|
85
|
-
end
|
86
|
-
end
|
87
|
-
module_function :realized?
|
88
|
-
|
89
|
-
def rejected?(object)
|
90
|
-
if object.respond_to?(:rejected?)
|
91
|
-
return object.rejected?
|
92
|
-
else
|
93
|
-
raise ArgumentError.new('object does not support #rejected?')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
module_function :rejected?
|
97
|
-
|
98
|
-
## promise
|
99
|
-
|
100
|
-
# Creates a new promise object. "A promise represents the eventual
|
101
|
-
# value returned from the single completion of an operation."
|
102
|
-
# Promises can be chained in a tree structure where each promise
|
103
|
-
# has zero or more children. Promises are resolved asynchronously
|
104
|
-
# in the order they are added to the tree. Parents are guaranteed
|
105
|
-
# to be resolved before their children. The result of each promise
|
106
|
-
# is passes to each of its children when the child resolves. When
|
107
|
-
# a promise is rejected all its children will be summarily rejected.
|
108
|
-
# A promise added to a rejected promise will immediately be rejected.
|
109
|
-
# A promise that is neither resolved or rejected is pending.
|
110
|
-
#
|
111
|
-
# @param args [Array] zero or more arguments for the block
|
112
|
-
# @param block [Proc] the block to call when attempting fulfillment
|
113
|
-
#
|
114
|
-
# @see Promise
|
115
|
-
# @see http://wiki.commonjs.org/wiki/Promises/A
|
116
|
-
def promise(*args, &block)
|
117
|
-
return Concurrent::Promise.new(*args, &block)
|
118
|
-
end
|
119
|
-
module_function :promise
|
120
|
-
end
|
1
|
+
require 'concurrent/agent'
|
2
|
+
require 'concurrent/defer'
|
3
|
+
require 'concurrent/future'
|
4
|
+
require 'concurrent/promise'
|
5
|
+
|
6
|
+
module Kernel
|
7
|
+
|
8
|
+
## agent
|
9
|
+
|
10
|
+
def agent(initial, timeout = Concurrent::Agent::TIMEOUT)
|
11
|
+
return Concurrent::Agent.new(initial, timeout)
|
12
|
+
end
|
13
|
+
module_function :agent
|
14
|
+
|
15
|
+
def post(object, &block)
|
16
|
+
if object.respond_to?(:post)
|
17
|
+
return object.post(&block)
|
18
|
+
else
|
19
|
+
raise ArgumentError.new('object does not support #post')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
module_function :post
|
23
|
+
|
24
|
+
## defer
|
25
|
+
|
26
|
+
def defer(*args, &block)
|
27
|
+
return Concurrent::Defer.new(*args, &block)
|
28
|
+
end
|
29
|
+
module_function :defer
|
30
|
+
|
31
|
+
## executor
|
32
|
+
|
33
|
+
def executor(*args, &block)
|
34
|
+
return Concurrent::Executor.run(*args, &block)
|
35
|
+
end
|
36
|
+
module_function :executor
|
37
|
+
|
38
|
+
## future
|
39
|
+
|
40
|
+
def future(*args, &block)
|
41
|
+
return Concurrent::Future.new(*args, &block)
|
42
|
+
end
|
43
|
+
module_function :future
|
44
|
+
|
45
|
+
## obligation
|
46
|
+
|
47
|
+
def deref(object, timeout = nil)
|
48
|
+
if object.respond_to?(:deref)
|
49
|
+
return object.deref(timeout)
|
50
|
+
elsif object.respond_to?(:value)
|
51
|
+
return object.value(timeout)
|
52
|
+
else
|
53
|
+
raise ArgumentError.new('object does not support #deref')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
module_function :deref
|
57
|
+
|
58
|
+
def pending?(object)
|
59
|
+
if object.respond_to?(:pending?)
|
60
|
+
return object.pending?
|
61
|
+
else
|
62
|
+
raise ArgumentError.new('object does not support #pending?')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
module_function :pending?
|
66
|
+
|
67
|
+
def fulfilled?(object)
|
68
|
+
if object.respond_to?(:fulfilled?)
|
69
|
+
return object.fulfilled?
|
70
|
+
elsif object.respond_to?(:realized?)
|
71
|
+
return object.realized?
|
72
|
+
else
|
73
|
+
raise ArgumentError.new('object does not support #fulfilled?')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
module_function :fulfilled?
|
77
|
+
|
78
|
+
def realized?(object)
|
79
|
+
if object.respond_to?(:realized?)
|
80
|
+
return object.realized?
|
81
|
+
elsif object.respond_to?(:fulfilled?)
|
82
|
+
return object.fulfilled?
|
83
|
+
else
|
84
|
+
raise ArgumentError.new('object does not support #realized?')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
module_function :realized?
|
88
|
+
|
89
|
+
def rejected?(object)
|
90
|
+
if object.respond_to?(:rejected?)
|
91
|
+
return object.rejected?
|
92
|
+
else
|
93
|
+
raise ArgumentError.new('object does not support #rejected?')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
module_function :rejected?
|
97
|
+
|
98
|
+
## promise
|
99
|
+
|
100
|
+
# Creates a new promise object. "A promise represents the eventual
|
101
|
+
# value returned from the single completion of an operation."
|
102
|
+
# Promises can be chained in a tree structure where each promise
|
103
|
+
# has zero or more children. Promises are resolved asynchronously
|
104
|
+
# in the order they are added to the tree. Parents are guaranteed
|
105
|
+
# to be resolved before their children. The result of each promise
|
106
|
+
# is passes to each of its children when the child resolves. When
|
107
|
+
# a promise is rejected all its children will be summarily rejected.
|
108
|
+
# A promise added to a rejected promise will immediately be rejected.
|
109
|
+
# A promise that is neither resolved or rejected is pending.
|
110
|
+
#
|
111
|
+
# @param args [Array] zero or more arguments for the block
|
112
|
+
# @param block [Proc] the block to call when attempting fulfillment
|
113
|
+
#
|
114
|
+
# @see Promise
|
115
|
+
# @see http://wiki.commonjs.org/wiki/Promises/A
|
116
|
+
def promise(*args, &block)
|
117
|
+
return Concurrent::Promise.new(*args, &block)
|
118
|
+
end
|
119
|
+
module_function :promise
|
120
|
+
end
|
data/lib/concurrent/future.rb
CHANGED
@@ -1,42 +1,42 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/global_thread_pool'
|
4
|
-
require 'concurrent/obligation'
|
5
|
-
require 'concurrent/utilities'
|
6
|
-
|
7
|
-
module Concurrent
|
8
|
-
|
9
|
-
class Future
|
10
|
-
include Obligation
|
11
|
-
include UsesGlobalThreadPool
|
12
|
-
|
13
|
-
behavior(:future)
|
14
|
-
|
15
|
-
def initialize(*args, &block)
|
16
|
-
unless block_given?
|
17
|
-
@state = :fulfilled
|
18
|
-
else
|
19
|
-
@value = nil
|
20
|
-
@state = :pending
|
21
|
-
Future.thread_pool.post(*args) do
|
22
|
-
work(*args, &block)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
# @private
|
30
|
-
def work(*args) # :nodoc:
|
31
|
-
mutex.synchronize do
|
32
|
-
begin
|
33
|
-
@value = yield(*args)
|
34
|
-
@state = :fulfilled
|
35
|
-
rescue Exception => ex
|
36
|
-
@state = :rejected
|
37
|
-
@reason = ex
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
require 'concurrent/global_thread_pool'
|
4
|
+
require 'concurrent/obligation'
|
5
|
+
require 'concurrent/utilities'
|
6
|
+
|
7
|
+
module Concurrent
|
8
|
+
|
9
|
+
class Future
|
10
|
+
include Obligation
|
11
|
+
include UsesGlobalThreadPool
|
12
|
+
|
13
|
+
behavior(:future)
|
14
|
+
|
15
|
+
def initialize(*args, &block)
|
16
|
+
unless block_given?
|
17
|
+
@state = :fulfilled
|
18
|
+
else
|
19
|
+
@value = nil
|
20
|
+
@state = :pending
|
21
|
+
Future.thread_pool.post(*args) do
|
22
|
+
work(*args, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @private
|
30
|
+
def work(*args) # :nodoc:
|
31
|
+
mutex.synchronize do
|
32
|
+
begin
|
33
|
+
@value = yield(*args)
|
34
|
+
@state = :fulfilled
|
35
|
+
rescue Exception => ex
|
36
|
+
@state = :rejected
|
37
|
+
@reason = ex
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|