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
@@ -0,0 +1,234 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe SmartMutex do
6
+
7
+ subject{ SmartMutex.new }
8
+
9
+ def fly_solo
10
+ Thread.stub(:list).and_return(Array.new)
11
+ end
12
+
13
+ def run_with_the_pack
14
+ Thread.stub(:list).and_return([1,2,3,4])
15
+ end
16
+
17
+ context '#initialize' do
18
+
19
+ it 'creates a new mutex' do
20
+ Mutex.should_receive(:new).with(no_args())
21
+ SmartMutex.new
22
+ end
23
+ end
24
+
25
+ context '#alone?' do
26
+
27
+ it 'returns true when there is only one thread' do
28
+ fly_solo
29
+ subject.alone?.should be_true
30
+ end
31
+
32
+ it 'returns false when there is more than one thread' do
33
+ run_with_the_pack
34
+ subject.alone?.should be_false
35
+ end
36
+ end
37
+
38
+ context '#lock' do
39
+
40
+ it 'does not lock when there is only one thread' do
41
+ fly_solo
42
+
43
+ mutex = Mutex.new
44
+ mutex.should_not_receive(:lock)
45
+ Mutex.should_receive(:new).with(no_args()).and_return(mutex)
46
+
47
+ subject.lock
48
+ end
49
+
50
+ it 'locks when not locked and there is more than one thread' do
51
+ run_with_the_pack
52
+
53
+ mutex = Mutex.new
54
+ mutex.should_receive(:lock)
55
+ Mutex.should_receive(:new).with(no_args()).and_return(mutex)
56
+
57
+ subject.lock
58
+ end
59
+
60
+ it 'raises an exception when locked and there is more than one thread' do
61
+ run_with_the_pack
62
+ subject.lock
63
+ lambda {
64
+ subject.lock
65
+ }.should raise_error(ThreadError)
66
+ end
67
+
68
+ it 'does not raise an exception when lock called twice and there is only one thread' do
69
+ fly_solo
70
+ subject.lock
71
+ lambda {
72
+ subject.lock
73
+ }.should_not raise_error
74
+ end
75
+
76
+ it 'returns self' do
77
+ fly_solo
78
+ mutex = SmartMutex.new
79
+ mutex.lock.should eq mutex
80
+
81
+ run_with_the_pack
82
+ mutex.lock.should eq mutex
83
+ end
84
+ end
85
+
86
+ context '#locked?' do
87
+
88
+ it 'returns false when there is only one thread' do
89
+ fly_solo
90
+ subject.should_not be_locked
91
+ end
92
+
93
+ it 'returns true when locked and there is more than one thread' do
94
+ run_with_the_pack
95
+ subject.lock
96
+ subject.should be_locked
97
+ end
98
+
99
+ it 'returns false when not locked and there is more than one thread' do
100
+ run_with_the_pack
101
+ subject.should_not be_locked
102
+ end
103
+ end
104
+
105
+ context '#sleep' do
106
+
107
+ it 'sleeps when there is only one thread' do
108
+ fly_solo
109
+ Kernel.should_receive(:sleep).with(0.1).and_return(0.1)
110
+ subject.sleep(0.1)
111
+ end
112
+
113
+ it 'sleeps when locked and there is more than one thread' do
114
+ mutex = Mutex.new
115
+ mutex.should_receive(:sleep).with(0.1).and_return(0.1)
116
+ Mutex.should_receive(:new).with(no_args()).and_return(mutex)
117
+
118
+ run_with_the_pack
119
+ subject.lock
120
+ subject.sleep(0.1)
121
+ end
122
+
123
+ it 'raises an exception when not locked and there is more than one thread' do
124
+ run_with_the_pack
125
+ lambda {
126
+ subject.sleep(0.1)
127
+ }.should raise_error(ThreadError)
128
+ end
129
+
130
+ it 'returns the number of seconds slept' do
131
+ fly_solo
132
+ subject.sleep(1).should eq 1
133
+
134
+ run_with_the_pack
135
+ subject.lock
136
+ subject.sleep(1).should eq 1
137
+ end
138
+ end
139
+
140
+ context '#synchronize' do
141
+
142
+ it 'yields to the block when there is only one thread' do
143
+ fly_solo
144
+ @expected = false
145
+ subject.synchronize{ @expected = true }
146
+ @expected.should be_true
147
+ end
148
+
149
+ it 'locks when there is more than one thread' do
150
+ mutex = Mutex.new
151
+ mutex.should_receive(:synchronize).with(no_args())
152
+ Mutex.should_receive(:new).with(no_args()).and_return(mutex)
153
+
154
+ run_with_the_pack
155
+ subject.synchronize{ nil }
156
+ end
157
+
158
+ it 'yields to the block when there is more than one thread' do
159
+ run_with_the_pack
160
+ @expected = false
161
+ subject.synchronize{ @expected = true }
162
+ @expected.should be_true
163
+ end
164
+
165
+ it 'returns the result of the block' do
166
+ fly_solo
167
+ subject.synchronize{ 42 }.should eq 42
168
+
169
+ run_with_the_pack
170
+ subject.synchronize{ 42 }.should eq 42
171
+ end
172
+ end
173
+
174
+ context '#try_lock' do
175
+
176
+ it 'returns true when there is only one thread' do
177
+ fly_solo
178
+ subject.try_lock.should be_true
179
+ end
180
+
181
+ it 'returns true when the lock is obtained and there is more than one thread' do
182
+ run_with_the_pack
183
+ subject.try_lock.should be_true
184
+ end
185
+
186
+ it 'returns false when the lock is not obtained and there is more than one thread' do
187
+ run_with_the_pack
188
+ subject.lock
189
+ subject.try_lock.should be_false
190
+ end
191
+ end
192
+
193
+ context '#unlock' do
194
+
195
+ it 'does not unlock when there is only one thread' do
196
+ fly_solo
197
+
198
+ mutex = Mutex.new
199
+ mutex.should_not_receive(:unlock)
200
+ Mutex.should_receive(:new).with(no_args()).and_return(mutex)
201
+
202
+ subject.unlock
203
+ end
204
+
205
+ it 'unlocks when locked and there is more than one thread' do
206
+ run_with_the_pack
207
+
208
+ mutex = Mutex.new
209
+ mutex.should_receive(:unlock)
210
+ Mutex.should_receive(:new).with(no_args()).and_return(mutex)
211
+
212
+ subject.lock
213
+ subject.unlock
214
+ end
215
+
216
+ it 'raises an exception when not locked and there is more than one thread' do
217
+ run_with_the_pack
218
+ lambda {
219
+ subject.unlock
220
+ }.should raise_error(ThreadError)
221
+ end
222
+
223
+ it 'returns self' do
224
+ fly_solo
225
+ mutex = SmartMutex.new
226
+ mutex.unlock.should eq mutex
227
+
228
+ run_with_the_pack
229
+ mutex.lock
230
+ mutex.unlock.should eq mutex
231
+ end
232
+ end
233
+ end
234
+ end
@@ -1,209 +1,209 @@
1
- require 'spec_helper'
2
-
3
- module Concurrent
4
-
5
- share_examples_for 'Thread Pool' do
6
-
7
- context '#running?' do
8
-
9
- it 'returns true when the thread pool is running' do
10
- subject.should be_running
11
- end
12
-
13
- it 'returns false when the thread pool is shutting down' do
14
- subject.post{ sleep(1) }
15
- subject.shutdown
16
- subject.should_not be_running
17
- end
18
-
19
- it 'returns false when the thread pool is shutdown' do
20
- subject.shutdown
21
- subject.should_not be_running
22
- end
23
-
24
- it 'returns false when the thread pool is killed' do
25
- subject.shutdown
26
- subject.should_not be_running
27
- end
28
- end
29
-
30
- context '#shutdown?' do
31
-
32
- it 'returns true if #shutdown has been called' do
33
- subject.shutdown
34
- subject.should be_shutdown
35
- end
36
-
37
- it 'returns false when running' do
38
- subject.should_not be_shutdown
39
- end
40
- end
41
-
42
- context '#killed?' do
43
-
44
- it 'returns true if tasks were killed at shutdown' do
45
- subject.post{ sleep(1) }
46
- subject.kill
47
- subject.should be_killed
48
- end
49
-
50
- it 'returns false when running' do
51
- subject.should_not be_killed
52
- end
53
- end
54
-
55
- context '#shutdown' do
56
-
57
- it 'stops accepting new tasks' do
58
- subject.post{ sleep(1) }
59
- subject.shutdown
60
- @expected = false
61
- subject.post{ @expected = true }.should be_false
62
- sleep(1)
63
- @expected.should be_false
64
- end
65
-
66
- it 'allows in-progress tasks to complete' do
67
- @expected = false
68
- subject.post{ sleep(0.5); @expected = true }
69
- subject.shutdown
70
- sleep(1)
71
- @expected.should be_true
72
- end
73
-
74
- it 'allows pending tasks to complete' do
75
- @expected = false
76
- subject.post{ sleep(0.2) }
77
- subject.post{ sleep(0.2); @expected = true }
78
- subject.shutdown
79
- sleep(1)
80
- @expected.should be_true
81
- end
82
-
83
- it 'allows threads to exit normally' do
84
- pool = FixedThreadPool.new(5)
85
- pool.shutdown
86
- sleep(1)
87
- pool.status.should be_empty
88
- end
89
- end
90
-
91
- context '#kill' do
92
-
93
- it 'stops accepting new tasks' do
94
- subject.post{ sleep(1) }
95
- subject.kill
96
- @expected = false
97
- subject.post{ @expected = true }.should be_false
98
- sleep(1)
99
- @expected.should be_false
100
- end
101
-
102
- it 'attempts to kill all in-progress tasks' do
103
- @expected = false
104
- subject.post{ sleep(1); @expected = true }
105
- subject.kill
106
- sleep(1)
107
- @expected.should be_false
108
- end
109
-
110
- it 'rejects all pending tasks' do
111
- @expected = false
112
- subject.post{ sleep(0.5) }
113
- subject.post{ sleep(0.5); @expected = true }
114
- subject.kill
115
- sleep(1)
116
- @expected.should be_false
117
- end
118
- end
119
-
120
- context '#wait_for_termination' do
121
-
122
- it 'immediately returns true after shutdown has complete' do
123
- subject.shutdown
124
- subject.wait_for_termination.should be_true
125
- end
126
-
127
- it 'blocks indefinitely when timeout it nil' do
128
- subject.post{ sleep(1) }
129
- subject.shutdown
130
- subject.wait_for_termination(nil).should be_true
131
- end
132
-
133
- it 'returns true when shutdown sucessfully completes before timeout' do
134
- subject.post{ sleep(0.5) }
135
- subject.shutdown
136
- subject.wait_for_termination(1).should be_true
137
- end
138
-
139
- it 'returns false when shutdown fails to complete before timeout' do
140
- subject.post{ sleep(1) }
141
- subject.shutdown
142
- subject.wait_for_termination(0.5).should be_true
143
- end
144
- end
145
-
146
- context '#post' do
147
-
148
- it 'raises an exception if no block is given' do
149
- lambda {
150
- subject.post
151
- }.should raise_error(ArgumentError)
152
- end
153
-
154
- it 'returns true when the block is added to the queue' do
155
- subject.post{ nil }.should be_true
156
- end
157
-
158
- it 'calls the block with the given arguments' do
159
- @expected = nil
160
- subject.post(1, 2, 3)do |a, b, c|
161
- @expected = a + b + c
162
- end
163
- sleep(0.1)
164
- @expected.should eq 6
165
- end
166
-
167
- it 'rejects the block while shutting down' do
168
- pool = FixedThreadPool.new(5)
169
- pool.post{ sleep(1) }
170
- pool.shutdown
171
- @expected = nil
172
- pool.post(1, 2, 3)do |a, b, c|
173
- @expected = a + b + c
174
- end
175
- @expected.should be_nil
176
- end
177
-
178
- it 'returns false while shutting down' do
179
- subject.post{ sleep(1) }
180
- subject.shutdown
181
- subject.post{ nil }.should be_false
182
- end
183
-
184
- it 'rejects the block once shutdown' do
185
- pool = FixedThreadPool.new(5)
186
- pool.shutdown
187
- @expected = nil
188
- pool.post(1, 2, 3)do |a, b, c|
189
- @expected = a + b + c
190
- end
191
- @expected.should be_nil
192
- end
193
-
194
- it 'returns false once shutdown' do
195
- subject.post{ nil }
196
- subject.shutdown
197
- sleep(0.1)
198
- subject.post{ nil }.should be_false
199
- end
200
-
201
- it 'aliases #<<' do
202
- @expected = false
203
- subject << proc { @expected = true }
204
- sleep(0.1)
205
- @expected.should be_true
206
- end
207
- end
208
- end
209
- end
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ share_examples_for 'Thread Pool' do
6
+
7
+ context '#running?' do
8
+
9
+ it 'returns true when the thread pool is running' do
10
+ subject.should be_running
11
+ end
12
+
13
+ it 'returns false when the thread pool is shutting down' do
14
+ subject.post{ sleep(1) }
15
+ subject.shutdown
16
+ subject.should_not be_running
17
+ end
18
+
19
+ it 'returns false when the thread pool is shutdown' do
20
+ subject.shutdown
21
+ subject.should_not be_running
22
+ end
23
+
24
+ it 'returns false when the thread pool is killed' do
25
+ subject.shutdown
26
+ subject.should_not be_running
27
+ end
28
+ end
29
+
30
+ context '#shutdown?' do
31
+
32
+ it 'returns true if #shutdown has been called' do
33
+ subject.shutdown
34
+ subject.should be_shutdown
35
+ end
36
+
37
+ it 'returns false when running' do
38
+ subject.should_not be_shutdown
39
+ end
40
+ end
41
+
42
+ context '#killed?' do
43
+
44
+ it 'returns true if tasks were killed at shutdown' do
45
+ subject.post{ sleep(1) }
46
+ subject.kill
47
+ subject.should be_killed
48
+ end
49
+
50
+ it 'returns false when running' do
51
+ subject.should_not be_killed
52
+ end
53
+ end
54
+
55
+ context '#shutdown' do
56
+
57
+ it 'stops accepting new tasks' do
58
+ subject.post{ sleep(1) }
59
+ subject.shutdown
60
+ @expected = false
61
+ subject.post{ @expected = true }.should be_false
62
+ sleep(1)
63
+ @expected.should be_false
64
+ end
65
+
66
+ it 'allows in-progress tasks to complete' do
67
+ @expected = false
68
+ subject.post{ sleep(0.5); @expected = true }
69
+ subject.shutdown
70
+ sleep(1)
71
+ @expected.should be_true
72
+ end
73
+
74
+ it 'allows pending tasks to complete' do
75
+ @expected = false
76
+ subject.post{ sleep(0.2) }
77
+ subject.post{ sleep(0.2); @expected = true }
78
+ subject.shutdown
79
+ sleep(1)
80
+ @expected.should be_true
81
+ end
82
+
83
+ it 'allows threads to exit normally' do
84
+ pool = FixedThreadPool.new(5)
85
+ pool.shutdown
86
+ sleep(1)
87
+ pool.status.should be_empty
88
+ end
89
+ end
90
+
91
+ context '#kill' do
92
+
93
+ it 'stops accepting new tasks' do
94
+ subject.post{ sleep(1) }
95
+ subject.kill
96
+ @expected = false
97
+ subject.post{ @expected = true }.should be_false
98
+ sleep(1)
99
+ @expected.should be_false
100
+ end
101
+
102
+ it 'attempts to kill all in-progress tasks' do
103
+ @expected = false
104
+ subject.post{ sleep(1); @expected = true }
105
+ subject.kill
106
+ sleep(1)
107
+ @expected.should be_false
108
+ end
109
+
110
+ it 'rejects all pending tasks' do
111
+ @expected = false
112
+ subject.post{ sleep(0.5) }
113
+ subject.post{ sleep(0.5); @expected = true }
114
+ subject.kill
115
+ sleep(1)
116
+ @expected.should be_false
117
+ end
118
+ end
119
+
120
+ context '#wait_for_termination' do
121
+
122
+ it 'immediately returns true after shutdown has complete' do
123
+ subject.shutdown
124
+ subject.wait_for_termination.should be_true
125
+ end
126
+
127
+ it 'blocks indefinitely when timeout it nil' do
128
+ subject.post{ sleep(1) }
129
+ subject.shutdown
130
+ subject.wait_for_termination(nil).should be_true
131
+ end
132
+
133
+ it 'returns true when shutdown sucessfully completes before timeout' do
134
+ subject.post{ sleep(0.5) }
135
+ subject.shutdown
136
+ subject.wait_for_termination(1).should be_true
137
+ end
138
+
139
+ it 'returns false when shutdown fails to complete before timeout' do
140
+ subject.post{ sleep(1) }
141
+ subject.shutdown
142
+ subject.wait_for_termination(0.5).should be_true
143
+ end
144
+ end
145
+
146
+ context '#post' do
147
+
148
+ it 'raises an exception if no block is given' do
149
+ lambda {
150
+ subject.post
151
+ }.should raise_error(ArgumentError)
152
+ end
153
+
154
+ it 'returns true when the block is added to the queue' do
155
+ subject.post{ nil }.should be_true
156
+ end
157
+
158
+ it 'calls the block with the given arguments' do
159
+ @expected = nil
160
+ subject.post(1, 2, 3)do |a, b, c|
161
+ @expected = a + b + c
162
+ end
163
+ sleep(0.1)
164
+ @expected.should eq 6
165
+ end
166
+
167
+ it 'rejects the block while shutting down' do
168
+ pool = FixedThreadPool.new(5)
169
+ pool.post{ sleep(1) }
170
+ pool.shutdown
171
+ @expected = nil
172
+ pool.post(1, 2, 3)do |a, b, c|
173
+ @expected = a + b + c
174
+ end
175
+ @expected.should be_nil
176
+ end
177
+
178
+ it 'returns false while shutting down' do
179
+ subject.post{ sleep(1) }
180
+ subject.shutdown
181
+ subject.post{ nil }.should be_false
182
+ end
183
+
184
+ it 'rejects the block once shutdown' do
185
+ pool = FixedThreadPool.new(5)
186
+ pool.shutdown
187
+ @expected = nil
188
+ pool.post(1, 2, 3)do |a, b, c|
189
+ @expected = a + b + c
190
+ end
191
+ @expected.should be_nil
192
+ end
193
+
194
+ it 'returns false once shutdown' do
195
+ subject.post{ nil }
196
+ subject.shutdown
197
+ sleep(0.1)
198
+ subject.post{ nil }.should be_false
199
+ end
200
+
201
+ it 'aliases #<<' do
202
+ @expected = false
203
+ subject << proc { @expected = true }
204
+ sleep(0.1)
205
+ @expected.should be_true
206
+ end
207
+ end
208
+ end
209
+ end