concurrent-ruby 0.5.0 → 0.6.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -77
  3. data/lib/concurrent.rb +17 -2
  4. data/lib/concurrent/actor.rb +17 -0
  5. data/lib/concurrent/actor_context.rb +31 -0
  6. data/lib/concurrent/actor_ref.rb +39 -0
  7. data/lib/concurrent/agent.rb +12 -3
  8. data/lib/concurrent/async.rb +290 -0
  9. data/lib/concurrent/atomic.rb +5 -9
  10. data/lib/concurrent/cached_thread_pool.rb +39 -137
  11. data/lib/concurrent/channel/blocking_ring_buffer.rb +60 -0
  12. data/lib/concurrent/channel/buffered_channel.rb +83 -0
  13. data/lib/concurrent/channel/channel.rb +11 -0
  14. data/lib/concurrent/channel/probe.rb +19 -0
  15. data/lib/concurrent/channel/ring_buffer.rb +54 -0
  16. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  17. data/lib/concurrent/channel/waitable_list.rb +38 -0
  18. data/lib/concurrent/configuration.rb +92 -0
  19. data/lib/concurrent/dataflow.rb +9 -3
  20. data/lib/concurrent/delay.rb +88 -0
  21. data/lib/concurrent/exchanger.rb +31 -0
  22. data/lib/concurrent/fixed_thread_pool.rb +28 -122
  23. data/lib/concurrent/future.rb +10 -5
  24. data/lib/concurrent/immediate_executor.rb +3 -2
  25. data/lib/concurrent/ivar.rb +2 -1
  26. data/lib/concurrent/java_cached_thread_pool.rb +45 -0
  27. data/lib/concurrent/java_fixed_thread_pool.rb +37 -0
  28. data/lib/concurrent/java_thread_pool_executor.rb +194 -0
  29. data/lib/concurrent/per_thread_executor.rb +23 -0
  30. data/lib/concurrent/postable.rb +2 -0
  31. data/lib/concurrent/processor_count.rb +125 -0
  32. data/lib/concurrent/promise.rb +42 -18
  33. data/lib/concurrent/ruby_cached_thread_pool.rb +37 -0
  34. data/lib/concurrent/ruby_fixed_thread_pool.rb +31 -0
  35. data/lib/concurrent/ruby_thread_pool_executor.rb +268 -0
  36. data/lib/concurrent/ruby_thread_pool_worker.rb +69 -0
  37. data/lib/concurrent/simple_actor_ref.rb +124 -0
  38. data/lib/concurrent/thread_local_var.rb +1 -1
  39. data/lib/concurrent/thread_pool_executor.rb +30 -0
  40. data/lib/concurrent/timer_task.rb +13 -10
  41. data/lib/concurrent/tvar.rb +212 -0
  42. data/lib/concurrent/utilities.rb +1 -0
  43. data/lib/concurrent/version.rb +1 -1
  44. data/spec/concurrent/actor_context_spec.rb +37 -0
  45. data/spec/concurrent/actor_ref_shared.rb +313 -0
  46. data/spec/concurrent/actor_spec.rb +9 -1
  47. data/spec/concurrent/agent_spec.rb +97 -96
  48. data/spec/concurrent/async_spec.rb +320 -0
  49. data/spec/concurrent/cached_thread_pool_shared.rb +137 -0
  50. data/spec/concurrent/channel/blocking_ring_buffer_spec.rb +149 -0
  51. data/spec/concurrent/channel/buffered_channel_spec.rb +151 -0
  52. data/spec/concurrent/channel/channel_spec.rb +37 -0
  53. data/spec/concurrent/channel/probe_spec.rb +49 -0
  54. data/spec/concurrent/channel/ring_buffer_spec.rb +126 -0
  55. data/spec/concurrent/channel/unbuffered_channel_spec.rb +132 -0
  56. data/spec/concurrent/configuration_spec.rb +134 -0
  57. data/spec/concurrent/dataflow_spec.rb +109 -27
  58. data/spec/concurrent/delay_spec.rb +77 -0
  59. data/spec/concurrent/exchanger_spec.rb +66 -0
  60. data/spec/concurrent/fixed_thread_pool_shared.rb +136 -0
  61. data/spec/concurrent/future_spec.rb +60 -51
  62. data/spec/concurrent/global_thread_pool_shared.rb +33 -0
  63. data/spec/concurrent/immediate_executor_spec.rb +4 -25
  64. data/spec/concurrent/ivar_spec.rb +36 -23
  65. data/spec/concurrent/java_cached_thread_pool_spec.rb +64 -0
  66. data/spec/concurrent/java_fixed_thread_pool_spec.rb +64 -0
  67. data/spec/concurrent/java_thread_pool_executor_spec.rb +71 -0
  68. data/spec/concurrent/obligation_shared.rb +32 -20
  69. data/spec/concurrent/{global_thread_pool_spec.rb → per_thread_executor_spec.rb} +9 -13
  70. data/spec/concurrent/processor_count_spec.rb +20 -0
  71. data/spec/concurrent/promise_spec.rb +29 -41
  72. data/spec/concurrent/ruby_cached_thread_pool_spec.rb +69 -0
  73. data/spec/concurrent/ruby_fixed_thread_pool_spec.rb +39 -0
  74. data/spec/concurrent/ruby_thread_pool_executor_spec.rb +183 -0
  75. data/spec/concurrent/simple_actor_ref_spec.rb +219 -0
  76. data/spec/concurrent/thread_pool_class_cast_spec.rb +40 -0
  77. data/spec/concurrent/thread_pool_executor_shared.rb +155 -0
  78. data/spec/concurrent/thread_pool_shared.rb +98 -36
  79. data/spec/concurrent/tvar_spec.rb +137 -0
  80. data/spec/spec_helper.rb +4 -0
  81. data/spec/support/functions.rb +4 -0
  82. metadata +85 -20
  83. data/lib/concurrent/cached_thread_pool/worker.rb +0 -91
  84. data/lib/concurrent/channel.rb +0 -63
  85. data/lib/concurrent/fixed_thread_pool/worker.rb +0 -54
  86. data/lib/concurrent/global_thread_pool.rb +0 -42
  87. data/spec/concurrent/cached_thread_pool_spec.rb +0 -101
  88. data/spec/concurrent/channel_spec.rb +0 -86
  89. data/spec/concurrent/fixed_thread_pool_spec.rb +0 -92
  90. data/spec/concurrent/uses_global_thread_pool_shared.rb +0 -64
@@ -0,0 +1,149 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe BlockingRingBuffer do
6
+
7
+ let(:capacity) { 3 }
8
+ let(:buffer) { BlockingRingBuffer.new(capacity) }
9
+
10
+ def fill_buffer
11
+ capacity.times { buffer.put 3 }
12
+ end
13
+
14
+ describe '#capacity' do
15
+ it 'returns the value passed in constructor' do
16
+ buffer.capacity.should eq capacity
17
+ end
18
+ end
19
+
20
+ describe '#count' do
21
+ it 'is zero when created' do
22
+ buffer.count.should eq 0
23
+ end
24
+
25
+ it 'increases when an element is added' do
26
+ buffer.put 5
27
+ buffer.count.should eq 1
28
+
29
+ buffer.put 1
30
+ buffer.count.should eq 2
31
+ end
32
+
33
+ it 'decreases when an element is removed' do
34
+ buffer.put 10
35
+
36
+ buffer.take
37
+
38
+ buffer.count.should eq 0
39
+ end
40
+ end
41
+
42
+ describe '#empty?' do
43
+ it 'is true when count is zero' do
44
+ buffer.empty?.should be_true
45
+ end
46
+
47
+ it 'is false when count is not zero' do
48
+ buffer.put 82
49
+ buffer.empty?.should be_false
50
+ end
51
+ end
52
+
53
+ describe '#full?' do
54
+ it 'is true when count is capacity' do
55
+ fill_buffer
56
+ buffer.full?.should be_true
57
+ end
58
+
59
+ it 'is false when count is not capacity' do
60
+ buffer.full?.should be_false
61
+ end
62
+ end
63
+
64
+ describe '#put' do
65
+ it 'block when buffer is full' do
66
+ fill_buffer
67
+
68
+ t = Thread.new { buffer.put 32 }
69
+
70
+ sleep(0.1)
71
+
72
+ t.status.should eq 'sleep'
73
+ end
74
+
75
+ it 'continues when an element is removed' do
76
+ latch = CountDownLatch.new(1)
77
+
78
+ Thread.new { (capacity + 1).times { buffer.put 'hi' }; latch.count_down }
79
+ Thread.new { sleep(0.1); buffer.take }
80
+
81
+ latch.wait(0.2).should be_true
82
+ end
83
+ end
84
+
85
+ describe '#take' do
86
+ it 'blocks when buffer is empty' do
87
+ t = Thread.new { buffer.take }
88
+
89
+ sleep(0.1)
90
+
91
+ t.status.should eq 'sleep'
92
+ end
93
+
94
+ it 'continues when an element is added' do
95
+ latch = CountDownLatch.new(1)
96
+
97
+ Thread.new { buffer.take; latch.count_down }
98
+ Thread.new { sleep(0.1); buffer.put 3 }
99
+
100
+ latch.wait(0.2).should be_true
101
+ end
102
+
103
+ it 'returns the first added value' do
104
+ buffer.put 'hi'
105
+ buffer.put 'foo'
106
+ buffer.put 'bar'
107
+
108
+ buffer.take.should eq 'hi'
109
+ buffer.take.should eq 'foo'
110
+ buffer.take.should eq 'bar'
111
+ end
112
+ end
113
+
114
+ describe '#peek' do
115
+ context 'buffer empty' do
116
+ it 'returns nil when buffer is empty' do
117
+ buffer.peek.should be_nil
118
+ end
119
+ end
120
+
121
+ context 'not empty' do
122
+
123
+ before(:each) { buffer.put 'element' }
124
+
125
+ it 'returns the first value' do
126
+ buffer.peek.should eq 'element'
127
+ end
128
+
129
+ it 'does not change buffer' do
130
+ buffer.peek
131
+ buffer.count.should eq 1
132
+ end
133
+ end
134
+ end
135
+
136
+ context 'circular condition' do
137
+ it 'can filled many times' do
138
+ fill_buffer
139
+ capacity.times { buffer.take }
140
+
141
+ buffer.put 'hi'
142
+
143
+ buffer.take.should eq 'hi'
144
+ buffer.capacity.should eq capacity
145
+ end
146
+ end
147
+
148
+ end
149
+ end
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe BufferedChannel do
6
+
7
+ let(:size) { 2 }
8
+ let!(:channel) { BufferedChannel.new(size) }
9
+ let(:probe) { Probe.new }
10
+
11
+ context 'without timeout' do
12
+
13
+ describe '#push' do
14
+ it 'adds elements to buffer' do
15
+ channel.buffer_queue_size.should be 0
16
+
17
+ channel.push('a')
18
+ channel.push('a')
19
+
20
+ channel.buffer_queue_size.should be 2
21
+ end
22
+
23
+ it 'should block when buffer is full' do
24
+ channel.push 1
25
+ channel.push 2
26
+
27
+ t = Thread.new { channel.push 3 }
28
+ sleep(0.05)
29
+ t.status.should eq 'sleep'
30
+ end
31
+
32
+ it 'restarts thread when buffer is no more full' do
33
+ channel.push 'hi'
34
+ channel.push 'foo'
35
+
36
+ result = nil
37
+
38
+ Thread.new { channel.push 'bar'; result = 42 }
39
+
40
+ sleep(0.1)
41
+
42
+ channel.pop
43
+
44
+ sleep(0.1)
45
+
46
+ result.should eq 42
47
+ end
48
+
49
+ it 'should assign value to a probe if probe set is not empty' do
50
+ channel.select(probe)
51
+ Thread.new { sleep(0.1); channel.push 3 }
52
+ probe.value.should eq 3
53
+ end
54
+ end
55
+
56
+ describe '#pop' do
57
+ it 'should block if buffer is empty' do
58
+ t = Thread.new { channel.pop }
59
+ sleep(0.05)
60
+ t.status.should eq 'sleep'
61
+ end
62
+
63
+ it 'returns value if buffer is not empty' do
64
+ channel.push 1
65
+ result = channel.pop
66
+
67
+ result.should eq 1
68
+ end
69
+
70
+ it 'removes the first value from the buffer' do
71
+ channel.push 'a'
72
+ channel.push 'b'
73
+
74
+ channel.pop.should eq 'a'
75
+ channel.buffer_queue_size.should eq 1
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ describe 'select' do
82
+
83
+ it 'does not block' do
84
+ t = Thread.new { channel.select(probe) }
85
+
86
+ sleep(0.05)
87
+
88
+ t.status.should eq false
89
+ end
90
+
91
+ it 'gets notified by writer thread' do
92
+ channel.select(probe)
93
+
94
+ Thread.new { channel.push 82 }
95
+
96
+ probe.value.should eq 82
97
+ end
98
+
99
+ end
100
+
101
+ context 'already set probes' do
102
+ context 'empty buffer' do
103
+ it 'discards already set probes' do
104
+ probe.set('set value')
105
+
106
+ channel.select(probe)
107
+
108
+ channel.push 27
109
+
110
+ channel.buffer_queue_size.should eq 1
111
+ channel.probe_set_size.should eq 0
112
+ end
113
+ end
114
+
115
+ context 'empty probe set' do
116
+ it 'discards set probe' do
117
+ probe.set('set value')
118
+
119
+ channel.push 82
120
+
121
+ channel.select(probe)
122
+
123
+ channel.buffer_queue_size.should eq 1
124
+
125
+ channel.pop.should eq 82
126
+
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'probe set' do
132
+
133
+ it 'has size zero after creation' do
134
+ channel.probe_set_size.should eq 0
135
+ end
136
+
137
+ it 'increases size after a select' do
138
+ channel.select(probe)
139
+ channel.probe_set_size.should eq 1
140
+ end
141
+
142
+ it 'decreases size after a removal' do
143
+ channel.select(probe)
144
+ channel.remove_probe(probe)
145
+ channel.probe_set_size.should eq 0
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Channel do
6
+
7
+ describe '.select' do
8
+
9
+ context 'without timeout' do
10
+ it 'returns the first value available on a channel' do
11
+ channels = [ UnbufferedChannel.new, UnbufferedChannel.new]
12
+
13
+ Thread.new { channels[1].push 77 }
14
+
15
+ value = Channel.select(*channels)
16
+
17
+ value.should eq 77
18
+ end
19
+
20
+ it 'cleans up' do
21
+ channels = [ UnbufferedChannel.new, UnbufferedChannel.new]
22
+ channels.each { |ch| ch.stub(:remove_probe).with( an_instance_of(Probe) )}
23
+
24
+ Thread.new { channels[1].push 77 }
25
+
26
+ value = Channel.select(*channels)
27
+
28
+ value.should eq 77
29
+
30
+ channels.each { |ch| expect(ch).to have_received(:remove_probe).with( an_instance_of(Probe) ) }
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Probe do
6
+
7
+ let(:probe) { Probe.new }
8
+
9
+ describe '#set_unless_assigned' do
10
+ context 'empty probe' do
11
+ it 'assigns the value' do
12
+ probe.set_unless_assigned(32)
13
+ probe.value.should eq 32
14
+ end
15
+
16
+ it 'returns true' do
17
+ probe.set_unless_assigned('hi').should eq true
18
+ end
19
+ end
20
+
21
+ context 'fulfilled probe' do
22
+ before(:each) { probe.set(27) }
23
+
24
+ it 'does not assign the value' do
25
+ probe.set_unless_assigned(88)
26
+ probe.value.should eq 27
27
+ end
28
+
29
+ it 'returns false' do
30
+ probe.set_unless_assigned('hello').should eq false
31
+ end
32
+ end
33
+
34
+ context 'rejected probe' do
35
+ before(:each) { probe.fail }
36
+
37
+ it 'does not assign the value' do
38
+ probe.set_unless_assigned(88)
39
+ probe.should be_rejected
40
+ end
41
+
42
+ it 'returns false' do
43
+ probe.set_unless_assigned('hello').should eq false
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe RingBuffer do
6
+
7
+ let(:capacity) { 3 }
8
+ let(:buffer) { RingBuffer.new(capacity) }
9
+
10
+ def fill_buffer
11
+ capacity.times { buffer.offer 3 }
12
+ end
13
+
14
+ describe '#capacity' do
15
+ it 'returns the value passed in constructor' do
16
+ buffer.capacity.should eq capacity
17
+ end
18
+ end
19
+
20
+ describe '#count' do
21
+ it 'is zero when created' do
22
+ buffer.count.should eq 0
23
+ end
24
+
25
+ it 'increases when an element is added' do
26
+ buffer.offer 5
27
+ buffer.count.should eq 1
28
+
29
+ buffer.offer 1
30
+ buffer.count.should eq 2
31
+ end
32
+
33
+ it 'decreases when an element is removed' do
34
+ buffer.offer 10
35
+ buffer.poll
36
+
37
+ buffer.count.should eq 0
38
+ end
39
+ end
40
+
41
+ describe '#empty?' do
42
+ it 'is true when count is zero' do
43
+ buffer.empty?.should be_true
44
+ end
45
+
46
+ it 'is false when count is not zero' do
47
+ buffer.offer 82
48
+ buffer.empty?.should be_false
49
+ end
50
+ end
51
+
52
+ describe '#full?' do
53
+ it 'is true when count is capacity' do
54
+ fill_buffer
55
+ buffer.full?.should be_true
56
+ end
57
+
58
+ it 'is false when count is not capacity' do
59
+ buffer.full?.should be_false
60
+ end
61
+ end
62
+
63
+ describe '#offer' do
64
+ it 'returns false when buffer is full' do
65
+ fill_buffer
66
+ buffer.offer(3).should be_false
67
+ end
68
+
69
+ it 'returns true when the buffer is not full' do
70
+ buffer.offer(5).should be_true
71
+ end
72
+
73
+ end
74
+
75
+ describe '#poll' do
76
+ it 'returns the first added value' do
77
+ buffer.offer 'hi'
78
+ buffer.offer 'foo'
79
+ buffer.offer 'bar'
80
+
81
+ buffer.poll.should eq 'hi'
82
+ buffer.poll.should eq 'foo'
83
+ buffer.poll.should eq 'bar'
84
+ end
85
+
86
+ it 'returns nil when buffer is empty' do
87
+ buffer.poll.should be_nil
88
+ end
89
+ end
90
+
91
+ describe '#peek' do
92
+ context 'buffer empty' do
93
+ it 'returns nil when buffer is empty' do
94
+ buffer.peek.should be_nil
95
+ end
96
+ end
97
+
98
+ context 'not empty' do
99
+
100
+ before(:each) { buffer.offer 'element' }
101
+
102
+ it 'returns the first value' do
103
+ buffer.peek.should eq 'element'
104
+ end
105
+
106
+ it 'does not change buffer' do
107
+ buffer.peek
108
+ buffer.count.should eq 1
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'circular condition' do
114
+ it 'can filled many times' do
115
+ fill_buffer
116
+ capacity.times { buffer.poll }
117
+
118
+ buffer.offer 'hi'
119
+
120
+ buffer.poll.should eq 'hi'
121
+ buffer.capacity.should eq capacity
122
+ end
123
+ end
124
+
125
+ end
126
+ end