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,134 +1,134 @@
1
- require 'spec_helper'
2
-
3
- module Concurrent
4
-
5
- describe Event do
6
-
7
- subject{ Event.new }
8
-
9
- context '#initialize' do
10
-
11
- it 'sets the state to unset' do
12
- subject.should_not be_set
13
- end
14
- end
15
-
16
- context '#set?' do
17
-
18
- it 'returns true when the event has been set' do
19
- subject.set
20
- subject.should be_set
21
- end
22
-
23
- it 'returns false if the event is unset' do
24
- subject.reset
25
- subject.should_not be_set
26
- end
27
- end
28
-
29
- context '#set' do
30
-
31
- it 'triggers the event' do
32
- subject.reset
33
- @expected = false
34
- Thread.new{ subject.wait; @expected = true }
35
- sleep(0.1)
36
- subject.set
37
- sleep(0.1)
38
- @expected.should be_true
39
- end
40
-
41
- it 'sets the state to set' do
42
- subject.set
43
- subject.should be_set
44
- end
45
- end
46
-
47
- context '#reset' do
48
-
49
- it 'sets the state to unset' do
50
- subject.set
51
- subject.should be_set
52
- subject.reset
53
- subject.should_not be_set
54
- end
55
- end
56
-
57
- context '#pulse' do
58
-
59
- it 'triggers the event' do
60
- subject.reset
61
- @expected = false
62
- Thread.new{ subject.wait; @expected = true }
63
- sleep(0.1)
64
- subject.pulse
65
- sleep(0.1)
66
- @expected.should be_true
67
- end
68
-
69
- it 'sets the state to unset' do
70
- subject.pulse
71
- sleep(0.1)
72
- subject.should_not be_set
73
- end
74
- end
75
-
76
- context '#wait' do
77
-
78
- it 'returns immediately when the event has been set' do
79
- subject.reset
80
- @expected = false
81
- subject.set
82
- Thread.new{ subject.wait(1000); @expected = true}
83
- sleep(1)
84
- @expected.should be_true
85
- end
86
-
87
- it 'returns true once the event is set' do
88
- subject.set
89
- subject.wait.should be_true
90
- end
91
-
92
- it 'blocks indefinitely when the timer is nil' do
93
- subject.reset
94
- @expected = false
95
- Thread.new{ subject.wait; @expected = true}
96
- subject.set
97
- sleep(1)
98
- @expected.should be_true
99
- end
100
-
101
- it 'stops waiting when the timer expires' do
102
- subject.reset
103
- @expected = false
104
- Thread.new{ subject.wait(0.5); @expected = true}
105
- sleep(1)
106
- @expected.should be_true
107
- end
108
-
109
- it 'returns false when the timer expires' do
110
- subject.reset
111
- subject.wait(1).should be_false
112
- end
113
-
114
- it 'triggers multiple waiting threads' do
115
- subject.reset
116
- @expected = []
117
- 5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
118
- subject.set
119
- sleep(1)
120
- @expected.length.should eq 5
121
- end
122
-
123
- it 'behaves appropriately if wait begins while #set is processing' do
124
- subject.reset
125
- @expected = []
126
- 5.times{ Thread.new{ subject.wait(5) } }
127
- subject.set
128
- 5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
129
- sleep(1)
130
- @expected.length.should eq 5
131
- end
132
- end
133
- end
134
- end
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Event do
6
+
7
+ subject{ Event.new }
8
+
9
+ context '#initialize' do
10
+
11
+ it 'sets the state to unset' do
12
+ subject.should_not be_set
13
+ end
14
+ end
15
+
16
+ context '#set?' do
17
+
18
+ it 'returns true when the event has been set' do
19
+ subject.set
20
+ subject.should be_set
21
+ end
22
+
23
+ it 'returns false if the event is unset' do
24
+ subject.reset
25
+ subject.should_not be_set
26
+ end
27
+ end
28
+
29
+ context '#set' do
30
+
31
+ it 'triggers the event' do
32
+ subject.reset
33
+ @expected = false
34
+ Thread.new{ subject.wait; @expected = true }
35
+ sleep(0.1)
36
+ subject.set
37
+ sleep(0.1)
38
+ @expected.should be_true
39
+ end
40
+
41
+ it 'sets the state to set' do
42
+ subject.set
43
+ subject.should be_set
44
+ end
45
+ end
46
+
47
+ context '#reset' do
48
+
49
+ it 'sets the state to unset' do
50
+ subject.set
51
+ subject.should be_set
52
+ subject.reset
53
+ subject.should_not be_set
54
+ end
55
+ end
56
+
57
+ context '#pulse' do
58
+
59
+ it 'triggers the event' do
60
+ subject.reset
61
+ @expected = false
62
+ Thread.new{ subject.wait; @expected = true }
63
+ sleep(0.1)
64
+ subject.pulse
65
+ sleep(0.1)
66
+ @expected.should be_true
67
+ end
68
+
69
+ it 'sets the state to unset' do
70
+ subject.pulse
71
+ sleep(0.1)
72
+ subject.should_not be_set
73
+ end
74
+ end
75
+
76
+ context '#wait' do
77
+
78
+ it 'returns immediately when the event has been set' do
79
+ subject.reset
80
+ @expected = false
81
+ subject.set
82
+ Thread.new{ subject.wait(1000); @expected = true}
83
+ sleep(1)
84
+ @expected.should be_true
85
+ end
86
+
87
+ it 'returns true once the event is set' do
88
+ subject.set
89
+ subject.wait.should be_true
90
+ end
91
+
92
+ it 'blocks indefinitely when the timer is nil' do
93
+ subject.reset
94
+ @expected = false
95
+ Thread.new{ subject.wait; @expected = true}
96
+ subject.set
97
+ sleep(1)
98
+ @expected.should be_true
99
+ end
100
+
101
+ it 'stops waiting when the timer expires' do
102
+ subject.reset
103
+ @expected = false
104
+ Thread.new{ subject.wait(0.5); @expected = true}
105
+ sleep(1)
106
+ @expected.should be_true
107
+ end
108
+
109
+ it 'returns false when the timer expires' do
110
+ subject.reset
111
+ subject.wait(1).should be_false
112
+ end
113
+
114
+ it 'triggers multiple waiting threads' do
115
+ subject.reset
116
+ @expected = []
117
+ 5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
118
+ subject.set
119
+ sleep(1)
120
+ @expected.length.should eq 5
121
+ end
122
+
123
+ it 'behaves appropriately if wait begins while #set is processing' do
124
+ subject.reset
125
+ @expected = []
126
+ 5.times{ Thread.new{ subject.wait(5) } }
127
+ subject.set
128
+ 5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
129
+ sleep(1)
130
+ @expected.length.should eq 5
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Executor do
6
+
7
+ before(:each) do
8
+ @orig_stdout = $stdout
9
+ $stdout = StringIO.new
10
+ end
11
+
12
+ after(:each) do
13
+ $stdout = @orig_stdout
14
+ end
15
+
16
+ after(:each) do
17
+ @ec.kill unless @ec.nil?
18
+ end
19
+
20
+ context '#run' do
21
+
22
+ it 'raises an exception if no block given' do
23
+ lambda {
24
+ @ec = Concurrent::Executor.run('Foo')
25
+ }.should raise_error
26
+ end
27
+
28
+ it 'uses the default execution interval when no interval is given' do
29
+ @ec = Executor.run('Foo'){ nil }
30
+ @ec.execution_interval.should eq Executor::EXECUTION_INTERVAL
31
+ end
32
+
33
+ it 'uses the default timeout interval when no interval is given' do
34
+ @ec = Executor.run('Foo'){ nil }
35
+ @ec.timeout_interval.should eq Executor::TIMEOUT_INTERVAL
36
+ end
37
+
38
+ it 'uses the given execution interval' do
39
+ @ec = Executor.run('Foo', execution_interval: 5){ nil }
40
+ @ec.execution_interval.should eq 5
41
+ end
42
+
43
+ it 'uses the given timeout interval' do
44
+ @ec = Executor.run('Foo', timeout_interval: 5){ nil }
45
+ @ec.timeout_interval.should eq 5
46
+ end
47
+
48
+ it 'creates a new thread' do
49
+ thread = Thread.new{ sleep(1) }
50
+ Thread.should_receive(:new).with(any_args()).and_return(thread)
51
+ @ec = Executor.run('Foo'){ nil }
52
+ end
53
+
54
+ it 'returns an ExecutionContext' do
55
+ @ec = Executor.run('Foo'){ nil }
56
+ @ec.should be_a(Executor::ExecutionContext)
57
+ end
58
+
59
+ it 'sets the #name context variable' do
60
+ @ec = Executor.run('Foo'){ nil }
61
+ @ec.name.should eq 'Foo'
62
+ end
63
+ end
64
+
65
+ context 'execution' do
66
+
67
+ it 'waits for #execution_interval seconds before executing the block' do
68
+ @expected = false
69
+ @ec = Executor.run('Foo', execution: 0.5){ @expected = true }
70
+ @expected.should be_false
71
+ sleep(1)
72
+ @expected.should be_true
73
+ end
74
+
75
+ it 'yields to the execution block' do
76
+ @expected = false
77
+ @ec = Executor.run('Foo', execution: 1){ @expected = true }
78
+ sleep(2)
79
+ @expected.should be_true
80
+ end
81
+
82
+ it 'passes any given arguments to the execution block' do
83
+ args = [1,2,3,4]
84
+ @expected = nil
85
+ @ec = Executor.run('Foo', execution_interval: 0.5, args: args) do |*args|
86
+ @expected = args
87
+ end
88
+ sleep(1)
89
+ @expected.should eq args
90
+ end
91
+
92
+ it 'supresses exceptions thrown by the execution block' do
93
+ lambda {
94
+ @ec = Executor.run('Foo', execution_interval: 0.5) { raise StandardError }
95
+ sleep(1)
96
+ }.should_not raise_error
97
+ end
98
+
99
+ it 'kills the worker thread if the timeout is reached' do
100
+ # the after(:each) block will trigger this expectation
101
+ Thread.should_receive(:kill).at_least(1).with(any_args())
102
+ @ec = Executor.run('Foo', execution_interval: 0.5, timeout_interval: 0.5){ Thread.stop }
103
+ sleep(1.5)
104
+ end
105
+ end
106
+
107
+ context 'logging' do
108
+
109
+ before(:each) do
110
+ @name = nil
111
+ @level = nil
112
+ @msg = nil
113
+
114
+ @logger = proc do |name, level, msg|
115
+ @name = name
116
+ @level = level
117
+ @msg = msg
118
+ end
119
+ end
120
+
121
+ it 'uses a custom logger when given' do
122
+ @ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ nil }
123
+ sleep(0.5)
124
+ @name.should eq 'Foo'
125
+ end
126
+
127
+ it 'logs :info when execution is successful' do
128
+ @ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ nil }
129
+ sleep(0.5)
130
+ @level.should eq :info
131
+ end
132
+
133
+ it 'logs :warn when execution times out' do
134
+ @ec = Executor.run('Foo', execution_interval: 0.1, timeout_interval: 0.1, logger: @logger){ Thread.stop }
135
+ sleep(0.5)
136
+ @level.should eq :warn
137
+ end
138
+
139
+ it 'logs :error when execution is fails' do
140
+ @ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ raise StandardError }
141
+ sleep(0.5)
142
+ @level.should eq :error
143
+ end
144
+ end
145
+ end
146
+ end
@@ -1,84 +1,84 @@
1
- require 'spec_helper'
2
- require_relative 'thread_pool_shared'
3
-
4
- module Concurrent
5
-
6
- describe FixedThreadPool do
7
-
8
- subject { FixedThreadPool.new(5) }
9
-
10
- it_should_behave_like 'Thread Pool'
11
-
12
- context '#initialize' do
13
-
14
- it 'raises an exception when the pool size is less than one' do
15
- lambda {
16
- FixedThreadPool.new(0)
17
- }.should raise_error(ArgumentError)
18
- end
19
-
20
- it 'raises an exception when the pool size is greater than 1024' do
21
- lambda {
22
- FixedThreadPool.new(1025)
23
- }.should raise_error(ArgumentError)
24
- end
25
-
26
- it 'creates a thread pool of the given size' do
27
- thread = double('thread')
28
- # add one for the garbage collector
29
- Thread.should_receive(:new).exactly(5+1).times.and_return(thread)
30
- pool = FixedThreadPool.new(5)
31
- pool.size.should eq 5
32
- end
33
-
34
- it 'aliases Concurrent#new_fixed_thread_pool' do
35
- pool = Concurrent.new_fixed_thread_pool(5)
36
- pool.should be_a(FixedThreadPool)
37
- pool.size.should eq 5
38
- end
39
- end
40
-
41
- context '#kill' do
42
-
43
- it 'kills all threads' do
44
- Thread.should_receive(:kill).exactly(5).times
45
- pool = FixedThreadPool.new(5)
46
- pool.kill
47
- sleep(0.1)
48
- end
49
- end
50
-
51
- context '#size' do
52
-
53
- let(:pool_size) { 3 }
54
- subject { FixedThreadPool.new(pool_size) }
55
-
56
- it 'returns the size of the subject when running' do
57
- subject.size.should eq pool_size
58
- end
59
-
60
- it 'returns zero while shutting down' do
61
- subject.post{ sleep(1) }
62
- subject.shutdown
63
- subject.size.should eq 0
64
- end
65
-
66
- it 'returns zero once shut down' do
67
- subject.shutdown
68
- subject.size.should eq 0
69
- end
70
- end
71
-
72
- context 'exception handling' do
73
-
74
- it 'restarts threads that experience exception' do
75
- pool = FixedThreadPool.new(5)
76
- 3.times{ pool << proc{ raise StandardError } }
77
- sleep(2)
78
- pool.size.should eq 5
79
- pool.status.should_not include(nil)
80
- #pool.status.include?(nil).should be_false
81
- end
82
- end
83
- end
84
- end
1
+ require 'spec_helper'
2
+ require_relative 'thread_pool_shared'
3
+
4
+ module Concurrent
5
+
6
+ describe FixedThreadPool do
7
+
8
+ subject { FixedThreadPool.new(5) }
9
+
10
+ it_should_behave_like 'Thread Pool'
11
+
12
+ context '#initialize' do
13
+
14
+ it 'raises an exception when the pool size is less than one' do
15
+ lambda {
16
+ FixedThreadPool.new(0)
17
+ }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'raises an exception when the pool size is greater than 1024' do
21
+ lambda {
22
+ FixedThreadPool.new(1025)
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ it 'creates a thread pool of the given size' do
27
+ thread = double('thread')
28
+ # add one for the garbage collector
29
+ Thread.should_receive(:new).exactly(5+1).times.and_return(thread)
30
+ pool = FixedThreadPool.new(5)
31
+ pool.size.should eq 5
32
+ end
33
+
34
+ it 'aliases Concurrent#new_fixed_thread_pool' do
35
+ pool = Concurrent.new_fixed_thread_pool(5)
36
+ pool.should be_a(FixedThreadPool)
37
+ pool.size.should eq 5
38
+ end
39
+ end
40
+
41
+ context '#kill' do
42
+
43
+ it 'kills all threads' do
44
+ Thread.should_receive(:kill).at_least(5).times
45
+ pool = FixedThreadPool.new(5)
46
+ pool.kill
47
+ sleep(0.1)
48
+ end
49
+ end
50
+
51
+ context '#size' do
52
+
53
+ let(:pool_size) { 3 }
54
+ subject { FixedThreadPool.new(pool_size) }
55
+
56
+ it 'returns the size of the subject when running' do
57
+ subject.size.should eq pool_size
58
+ end
59
+
60
+ it 'returns zero while shutting down' do
61
+ subject.post{ sleep(1) }
62
+ subject.shutdown
63
+ subject.size.should eq 0
64
+ end
65
+
66
+ it 'returns zero once shut down' do
67
+ subject.shutdown
68
+ subject.size.should eq 0
69
+ end
70
+ end
71
+
72
+ context 'exception handling' do
73
+
74
+ it 'restarts threads that experience exception' do
75
+ pool = FixedThreadPool.new(5)
76
+ 3.times{ pool << proc{ raise StandardError } }
77
+ sleep(2)
78
+ pool.size.should eq 5
79
+ pool.status.should_not include(nil)
80
+ #pool.status.include?(nil).should be_false
81
+ end
82
+ end
83
+ end
84
+ end