concurrent-ruby 0.1.0 → 0.1.1.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/LICENSE +21 -21
  2. data/README.md +279 -224
  3. data/lib/concurrent.rb +27 -20
  4. data/lib/concurrent/agent.rb +106 -130
  5. data/lib/concurrent/cached_thread_pool.rb +130 -122
  6. data/lib/concurrent/defer.rb +67 -69
  7. data/lib/concurrent/drb_async_demux.rb +72 -0
  8. data/lib/concurrent/event.rb +60 -60
  9. data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
  10. data/lib/concurrent/executor.rb +87 -0
  11. data/lib/concurrent/fixed_thread_pool.rb +89 -89
  12. data/lib/concurrent/functions.rb +120 -0
  13. data/lib/concurrent/future.rb +52 -42
  14. data/lib/concurrent/global_thread_pool.rb +3 -3
  15. data/lib/concurrent/goroutine.rb +29 -25
  16. data/lib/concurrent/obligation.rb +67 -121
  17. data/lib/concurrent/promise.rb +172 -194
  18. data/lib/concurrent/reactor.rb +162 -0
  19. data/lib/concurrent/smart_mutex.rb +66 -0
  20. data/lib/concurrent/tcp_sync_demux.rb +96 -0
  21. data/lib/concurrent/thread_pool.rb +65 -61
  22. data/lib/concurrent/utilities.rb +34 -0
  23. data/lib/concurrent/version.rb +3 -3
  24. data/lib/concurrent_ruby.rb +1 -1
  25. data/md/agent.md +123 -123
  26. data/md/defer.md +174 -174
  27. data/md/event.md +32 -32
  28. data/md/executor.md +176 -0
  29. data/md/future.md +83 -83
  30. data/md/goroutine.md +52 -52
  31. data/md/obligation.md +32 -32
  32. data/md/promise.md +225 -225
  33. data/md/thread_pool.md +197 -197
  34. data/spec/concurrent/agent_spec.rb +376 -405
  35. data/spec/concurrent/cached_thread_pool_spec.rb +112 -112
  36. data/spec/concurrent/defer_spec.rb +209 -199
  37. data/spec/concurrent/event_machine_defer_proxy_spec.rb +250 -246
  38. data/spec/concurrent/event_spec.rb +134 -134
  39. data/spec/concurrent/executor_spec.rb +146 -0
  40. data/spec/concurrent/fixed_thread_pool_spec.rb +84 -84
  41. data/spec/concurrent/functions_spec.rb +57 -0
  42. data/spec/concurrent/future_spec.rb +125 -115
  43. data/spec/concurrent/goroutine_spec.rb +67 -52
  44. data/spec/concurrent/obligation_shared.rb +121 -121
  45. data/spec/concurrent/promise_spec.rb +299 -310
  46. data/spec/concurrent/smart_mutex_spec.rb +234 -0
  47. data/spec/concurrent/thread_pool_shared.rb +209 -209
  48. data/spec/concurrent/utilities_spec.rb +74 -0
  49. data/spec/spec_helper.rb +21 -19
  50. metadata +38 -14
  51. checksums.yaml +0 -7
@@ -1,112 +1,112 @@
1
- require 'spec_helper'
2
- require_relative 'thread_pool_shared'
3
-
4
- module Concurrent
5
-
6
- describe CachedThreadPool do
7
-
8
- subject { CachedThreadPool.new }
9
-
10
- it_should_behave_like 'Thread Pool'
11
-
12
- context '#initialize' do
13
- it 'aliases Concurrent#new_cached_thread_pool' do
14
- pool = Concurrent.new_cached_thread_pool
15
- pool.should be_a(CachedThreadPool)
16
- pool.size.should eq 0
17
- end
18
- end
19
-
20
- context '#kill' do
21
-
22
- it 'kills all threads' do
23
- Thread.should_receive(:kill).exactly(5).times
24
- pool = CachedThreadPool.new
25
- 5.times{ sleep(0.1); pool << proc{ sleep(1) } }
26
- sleep(1)
27
- pool.kill
28
- sleep(0.1)
29
- end
30
- end
31
-
32
- context '#size' do
33
-
34
- it 'returns zero for a new thread pool' do
35
- subject.size.should eq 0
36
- end
37
-
38
- it 'returns the size of the subject when running' do
39
- 5.times{ sleep(0.1); subject << proc{ sleep(1) } }
40
- subject.size.should eq 5
41
- end
42
-
43
- it 'returns zero once shut down' do
44
- subject.shutdown
45
- subject.size.should eq 0
46
- end
47
- end
48
-
49
- context 'worker creation and caching' do
50
-
51
- it 'creates new workers when there are none available' do
52
- subject.size.should eq 0
53
- 5.times{ sleep(0.1); subject << proc{ sleep(1000) } }
54
- sleep(1)
55
- subject.size.should eq 5
56
- end
57
-
58
- it 'uses existing idle threads' do
59
- 5.times{ sleep(0.05); subject << proc{ sleep(0.5) } }
60
- sleep(1)
61
- 3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
62
- subject.size.should eq 5
63
- end
64
- end
65
-
66
- context 'garbage collection' do
67
-
68
- subject{ CachedThreadPool.new(gc_interval: 1, thread_idleime: 1) }
69
-
70
- it 'starts when the first thread is added to the pool' do
71
- subject.should_receive(:collect_garbage)
72
- subject << proc{ nil }
73
- sleep(0.1)
74
- end
75
-
76
- it 'removes from pool any thread that has been idle too long' do
77
- subject << proc{ nil }
78
- subject.size.should eq 1
79
- sleep(1.5)
80
- subject.size.should eq 0
81
- end
82
-
83
- it 'removed from pool any dead thread' do
84
- subject << proc{ raise StandardError }
85
- subject.size.should eq 1
86
- sleep(1.5)
87
- subject.size.should eq 0
88
- end
89
-
90
- it 'resets the working count appropriately' do
91
- subject << proc{ sleep(1000) }
92
- sleep(0.1)
93
- subject << proc{ raise StandardError }
94
- sleep(0.1)
95
- subject << proc{ nil }
96
-
97
- sleep(0.1)
98
- subject.working.should eq 2
99
-
100
- sleep(1.5)
101
- subject.working.should eq 1
102
- end
103
-
104
- it 'stops collection when the pool size becomes zero' do
105
- 3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
106
- subject.instance_variable_get(:@collector).status.should eq 'sleep'
107
- sleep(1.5)
108
- subject.instance_variable_get(:@collector).status.should be_false
109
- end
110
- end
111
- end
112
- end
1
+ require 'spec_helper'
2
+ require_relative 'thread_pool_shared'
3
+
4
+ module Concurrent
5
+
6
+ describe CachedThreadPool do
7
+
8
+ subject { CachedThreadPool.new }
9
+
10
+ it_should_behave_like 'Thread Pool'
11
+
12
+ context '#initialize' do
13
+ it 'aliases Concurrent#new_cached_thread_pool' do
14
+ pool = Concurrent.new_cached_thread_pool
15
+ pool.should be_a(CachedThreadPool)
16
+ pool.size.should eq 0
17
+ end
18
+ end
19
+
20
+ context '#kill' do
21
+
22
+ it 'kills all threads' do
23
+ Thread.should_receive(:kill).at_least(5).times
24
+ pool = CachedThreadPool.new
25
+ 5.times{ sleep(0.1); pool << proc{ sleep(1) } }
26
+ sleep(1)
27
+ pool.kill
28
+ sleep(0.1)
29
+ end
30
+ end
31
+
32
+ context '#size' do
33
+
34
+ it 'returns zero for a new thread pool' do
35
+ subject.size.should eq 0
36
+ end
37
+
38
+ it 'returns the size of the subject when running' do
39
+ 5.times{ sleep(0.1); subject << proc{ sleep(1) } }
40
+ subject.size.should eq 5
41
+ end
42
+
43
+ it 'returns zero once shut down' do
44
+ subject.shutdown
45
+ subject.size.should eq 0
46
+ end
47
+ end
48
+
49
+ context 'worker creation and caching' do
50
+
51
+ it 'creates new workers when there are none available' do
52
+ subject.size.should eq 0
53
+ 5.times{ sleep(0.1); subject << proc{ sleep(1000) } }
54
+ sleep(1)
55
+ subject.size.should eq 5
56
+ end
57
+
58
+ it 'uses existing idle threads' do
59
+ 5.times{ sleep(0.05); subject << proc{ sleep(0.5) } }
60
+ sleep(1)
61
+ 3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
62
+ subject.size.should eq 5
63
+ end
64
+ end
65
+
66
+ context 'garbage collection' do
67
+
68
+ subject{ CachedThreadPool.new(gc_interval: 1, thread_idleime: 1) }
69
+
70
+ it 'starts when the first thread is added to the pool' do
71
+ subject.should_receive(:collect_garbage)
72
+ subject << proc{ nil }
73
+ sleep(0.1)
74
+ end
75
+
76
+ it 'removes from pool any thread that has been idle too long' do
77
+ subject << proc{ nil }
78
+ subject.size.should eq 1
79
+ sleep(1.5)
80
+ subject.size.should eq 0
81
+ end
82
+
83
+ it 'removed from pool any dead thread' do
84
+ subject << proc{ raise StandardError }
85
+ subject.size.should eq 1
86
+ sleep(1.5)
87
+ subject.size.should eq 0
88
+ end
89
+
90
+ it 'resets the working count appropriately' do
91
+ subject << proc{ sleep(1000) }
92
+ sleep(0.1)
93
+ subject << proc{ raise StandardError }
94
+ sleep(0.1)
95
+ subject << proc{ nil }
96
+
97
+ sleep(0.1)
98
+ subject.working.should eq 2
99
+
100
+ sleep(1.5)
101
+ subject.working.should eq 1
102
+ end
103
+
104
+ it 'stops collection when the pool size becomes zero' do
105
+ 3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
106
+ subject.instance_variable_get(:@collector).status.should eq 'sleep'
107
+ sleep(1.5)
108
+ subject.instance_variable_get(:@collector).status.should be_false
109
+ end
110
+ end
111
+ end
112
+ end
@@ -1,199 +1,209 @@
1
- require 'spec_helper'
2
-
3
- module Concurrent
4
-
5
- describe Defer do
6
-
7
- context '#initialize' do
8
-
9
- before(:each) do
10
- $GLOBAL_THREAD_POOL = FixedThreadPool.new(1)
11
- end
12
-
13
- it 'raises an exception if no block or operation given' do
14
- lambda {
15
- Defer.new
16
- }.should raise_error(ArgumentError)
17
- end
18
-
19
- it 'raises an exception if both a block and an operation given' do
20
- lambda {
21
- operation = proc{ nil }
22
- Defer.new(operation, nil, nil){ nil }
23
- }.should raise_error(ArgumentError)
24
- end
25
-
26
- it 'starts the thread if an operation is given' do
27
- $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
28
- operation = proc{ nil }
29
- Defer.new(operation, nil, nil)
30
- end
31
-
32
- it 'does not start the thread if neither a callback or errorback is given' do
33
- $GLOBAL_THREAD_POOL.should_not_receive(:post)
34
- Defer.new(nil, nil, nil){ nil }
35
- end
36
-
37
- it 'aliases Kernel#defer' do
38
- defer{ nil }.should be_a(Defer)
39
- end
40
- end
41
-
42
- context '#then' do
43
-
44
- it 'raises an exception if no block given' do
45
- lambda {
46
- Defer.new{ nil }.then
47
- }.should raise_error(ArgumentError)
48
- end
49
-
50
- it 'raises an exception if called twice' do
51
- lambda {
52
- Defer.new{ nil }.then{|result| nil }.then{|result| nil }
53
- }.should raise_error(IllegalMethodCallError)
54
- end
55
-
56
- it 'raises an exception if an operation was provided at construction' do
57
- lambda {
58
- operation = proc{ nil }
59
- Defer.new(operation, nil, nil).then{|result| nil }
60
- }.should raise_error(IllegalMethodCallError)
61
- end
62
-
63
- it 'raises an exception if a callback was provided at construction' do
64
- lambda {
65
- callback = proc{|result|nil }
66
- Defer.new(nil, callback, nil){ nil }.then{|result| nil }
67
- }.should raise_error(IllegalMethodCallError)
68
- end
69
-
70
- it 'returns self' do
71
- deferred = Defer.new{ nil }
72
- deferred.then{|result| nil }.should eq deferred
73
- end
74
- end
75
-
76
- context '#rescue' do
77
-
78
- it 'raises an exception if no block given' do
79
- lambda {
80
- Defer.new{ nil }.rescue
81
- }.should raise_error(ArgumentError)
82
- end
83
-
84
- it 'raises an exception if called twice' do
85
- lambda {
86
- Defer.new{ nil }.rescue{ nil }.rescue{ nil }
87
- }.should raise_error(IllegalMethodCallError)
88
- end
89
-
90
- it 'raises an exception if an operation was provided at construction' do
91
- lambda {
92
- operation = proc{ nil }
93
- Defer.new(operation, nil, nil).rescue{|ex| nil }
94
- }.should raise_error(IllegalMethodCallError)
95
- end
96
-
97
- it 'raises an exception if an errorback was provided at construction' do
98
- lambda {
99
- errorback = proc{|ex| nil }
100
- Defer.new(nil, nil, errorback){ nil }.rescue{|ex| nil }
101
- }.should raise_error(IllegalMethodCallError)
102
- end
103
-
104
- it 'returns self' do
105
- deferred = Defer.new{ nil }
106
- deferred.rescue{|ex| nil }.should eq deferred
107
- end
108
-
109
- it 'aliases #catch' do
110
- lambda {
111
- Defer.new{ nil }.catch{|ex| nil }
112
- }.should_not raise_error
113
- end
114
-
115
- it 'aliases #on_error' do
116
- lambda {
117
- Defer.new{ nil }.on_error{|ex| nil }
118
- }.should_not raise_error
119
- end
120
- end
121
-
122
- context '#go' do
123
-
124
- it 'starts the thread if not started' do
125
- deferred = Defer.new{ nil }
126
- $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
127
- deferred.go
128
- end
129
-
130
- it 'does nothing if called more than once' do
131
- deferred = Defer.new{ nil }
132
- deferred.go
133
- $GLOBAL_THREAD_POOL.should_not_receive(:post)
134
- deferred.go
135
- end
136
-
137
- it 'does nothing if thread started at construction' do
138
- operation = proc{ nil }
139
- callback = proc{|result| nil }
140
- errorback = proc{|ex| nil }
141
- deferred = Defer.new(operation, callback, errorback)
142
- $GLOBAL_THREAD_POOL.should_not_receive(:post)
143
- deferred.go
144
- end
145
- end
146
-
147
- context 'fulfillment' do
148
-
149
- it 'runs the operation' do
150
- @expected = false
151
- Defer.new{ @expected = true }.go
152
- sleep(0.1)
153
- @expected.should be_true
154
- end
155
-
156
- it 'calls the callback when the operation is successful' do
157
- @expected = false
158
- Defer.new{ true }.then{|result| @expected = true }.go
159
- sleep(0.1)
160
- @expected.should be_true
161
- end
162
-
163
- it 'passes the result of the block to the callback' do
164
- @expected = false
165
- Defer.new{ 'w00t' }.then{|result| @expected = result }.go
166
- sleep(0.1)
167
- @expected.should eq 'w00t'
168
- end
169
-
170
- it 'does not call the errorback when the operation is successful' do
171
- @expected = true
172
- Defer.new{ nil }.rescue{|ex| @expected = false }.go
173
- sleep(0.1)
174
- @expected.should be_true
175
- end
176
-
177
- it 'calls the errorback if the operation throws an exception' do
178
- @expected = false
179
- Defer.new{ raise StandardError }.rescue{|ex| @expected = true }.go
180
- sleep(0.1)
181
- @expected.should be_true
182
- end
183
-
184
- it 'passes the exception object to the errorback' do
185
- @expected = nil
186
- Defer.new{ raise StandardError }.rescue{|ex| @expected = ex }.go
187
- sleep(0.1)
188
- @expected.should be_a(StandardError)
189
- end
190
-
191
- it 'does not call the callback when the operation fails' do
192
- @expected = true
193
- Defer.new{ raise StandardError }.then{|result| @expected = false }.go
194
- sleep(0.1)
195
- @expected.should be_true
196
- end
197
- end
198
- end
199
- end
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Defer do
6
+
7
+ context '#initialize' do
8
+
9
+ before(:each) do
10
+ $GLOBAL_THREAD_POOL = FixedThreadPool.new(1)
11
+ end
12
+
13
+ it 'raises an exception if no block or operation given' do
14
+ lambda {
15
+ Defer.new
16
+ }.should raise_error(ArgumentError)
17
+ end
18
+
19
+ it 'raises an exception if both a block and an operation given' do
20
+ lambda {
21
+ operation = proc{ nil }
22
+ Defer.new(op: operation){ nil }
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ it 'starts the thread if an operation is given' do
27
+ $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
28
+ operation = proc{ nil }
29
+ Defer.new(op: operation)
30
+ end
31
+
32
+ it 'does not start the thread if neither a callback or errorback is given' do
33
+ $GLOBAL_THREAD_POOL.should_not_receive(:post)
34
+ Defer.new{ nil }
35
+ end
36
+
37
+ it 'runs on the alternate thread pool if given' do
38
+ operation = proc{ nil }
39
+ pool = Concurrent::FixedThreadPool.new(2)
40
+ pool.should_receive(:post).with(no_args())
41
+ Defer.new(op: operation, pool: pool)
42
+ end
43
+ end
44
+
45
+ context '#then' do
46
+
47
+ it 'raises an exception if no block given' do
48
+ lambda {
49
+ Defer.new{ nil }.then
50
+ }.should raise_error(ArgumentError)
51
+ end
52
+
53
+ it 'raises an exception if called twice' do
54
+ lambda {
55
+ Defer.new{ nil }.then{|result| nil }.then{|result| nil }
56
+ }.should raise_error(IllegalMethodCallError)
57
+ end
58
+
59
+ it 'raises an exception if an operation was provided at construction' do
60
+ lambda {
61
+ operation = proc{ nil }
62
+ Defer.new(op: operation).then{|result| nil }
63
+ }.should raise_error(IllegalMethodCallError)
64
+ end
65
+
66
+ it 'raises an exception if a callback was provided at construction' do
67
+ lambda {
68
+ callback = proc{|result|nil }
69
+ Defer.new(callback: callback){ nil }.then{|result| nil }
70
+ }.should raise_error(IllegalMethodCallError)
71
+ end
72
+
73
+ it 'returns self' do
74
+ deferred = Defer.new{ nil }
75
+ deferred.then{|result| nil }.should eq deferred
76
+ end
77
+ end
78
+
79
+ context '#rescue' do
80
+
81
+ it 'raises an exception if no block given' do
82
+ lambda {
83
+ Defer.new{ nil }.rescue
84
+ }.should raise_error(ArgumentError)
85
+ end
86
+
87
+ it 'raises an exception if called twice' do
88
+ lambda {
89
+ Defer.new{ nil }.rescue{ nil }.rescue{ nil }
90
+ }.should raise_error(IllegalMethodCallError)
91
+ end
92
+
93
+ it 'raises an exception if an operation was provided at construction' do
94
+ lambda {
95
+ operation = proc{ nil }
96
+ Defer.new(op: operation).rescue{|ex| nil }
97
+ }.should raise_error(IllegalMethodCallError)
98
+ end
99
+
100
+ it 'raises an exception if an errorback was provided at construction' do
101
+ lambda {
102
+ errorback = proc{|ex| nil }
103
+ Defer.new(errorback: errorback){ nil }.rescue{|ex| nil }
104
+ }.should raise_error(IllegalMethodCallError)
105
+ end
106
+
107
+ it 'returns self' do
108
+ deferred = Defer.new{ nil }
109
+ deferred.rescue{|ex| nil }.should eq deferred
110
+ end
111
+
112
+ it 'aliases #catch' do
113
+ lambda {
114
+ Defer.new{ nil }.catch{|ex| nil }
115
+ }.should_not raise_error
116
+ end
117
+
118
+ it 'aliases #on_error' do
119
+ lambda {
120
+ Defer.new{ nil }.on_error{|ex| nil }
121
+ }.should_not raise_error
122
+ end
123
+ end
124
+
125
+ context '#go' do
126
+
127
+ it 'starts the thread if not started' do
128
+ deferred = Defer.new{ nil }
129
+ $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
130
+ deferred.go
131
+ end
132
+
133
+ it 'does nothing if called more than once' do
134
+ deferred = Defer.new{ nil }
135
+ deferred.go
136
+ $GLOBAL_THREAD_POOL.should_not_receive(:post)
137
+ deferred.go
138
+ end
139
+
140
+ it 'does nothing if thread started at construction' do
141
+ operation = proc{ nil }
142
+ callback = proc{|result| nil }
143
+ errorback = proc{|ex| nil }
144
+ deferred = Defer.new(op: operation, callback: callback, errorback: errorback)
145
+ $GLOBAL_THREAD_POOL.should_not_receive(:post)
146
+ deferred.go
147
+ end
148
+
149
+ it 'runs on the alternate thread pool if given' do
150
+ pool = Concurrent::FixedThreadPool.new(2)
151
+ pool.should_receive(:post).with(no_args())
152
+ deferred = Defer.new{ nil }
153
+ deferred.go(pool)
154
+ end
155
+ end
156
+
157
+ context 'fulfillment' do
158
+
159
+ it 'runs the operation' do
160
+ @expected = false
161
+ Defer.new{ @expected = true }.go
162
+ sleep(0.1)
163
+ @expected.should be_true
164
+ end
165
+
166
+ it 'calls the callback when the operation is successful' do
167
+ @expected = false
168
+ Defer.new{ true }.then{|result| @expected = true }.go
169
+ sleep(0.1)
170
+ @expected.should be_true
171
+ end
172
+
173
+ it 'passes the result of the block to the callback' do
174
+ @expected = false
175
+ Defer.new{ 'w00t' }.then{|result| @expected = result }.go
176
+ sleep(0.1)
177
+ @expected.should eq 'w00t'
178
+ end
179
+
180
+ it 'does not call the errorback when the operation is successful' do
181
+ @expected = true
182
+ Defer.new{ nil }.rescue{|ex| @expected = false }.go
183
+ sleep(0.1)
184
+ @expected.should be_true
185
+ end
186
+
187
+ it 'calls the errorback if the operation throws an exception' do
188
+ @expected = false
189
+ Defer.new{ raise StandardError }.rescue{|ex| @expected = true }.go
190
+ sleep(0.1)
191
+ @expected.should be_true
192
+ end
193
+
194
+ it 'passes the exception object to the errorback' do
195
+ @expected = nil
196
+ Defer.new{ raise StandardError }.rescue{|ex| @expected = ex }.go
197
+ sleep(0.1)
198
+ @expected.should be_a(StandardError)
199
+ end
200
+
201
+ it 'does not call the callback when the operation fails' do
202
+ @expected = true
203
+ Defer.new{ raise StandardError }.then{|result| @expected = false }.go
204
+ sleep(0.1)
205
+ @expected.should be_true
206
+ end
207
+ end
208
+ end
209
+ end