concurrent-ruby 0.1.0 → 0.1.1.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.
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