concurrent-ruby 0.5.0 → 0.6.0.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 (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