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,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