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,33 @@
1
+ require 'spec_helper'
2
+
3
+ share_examples_for :global_thread_pool do
4
+
5
+ context '#post' do
6
+
7
+ it 'raises an exception if no block is given' do
8
+ lambda {
9
+ subject.post
10
+ }.should raise_error(ArgumentError)
11
+ end
12
+
13
+ it 'returns true when the block is added to the queue' do
14
+ subject.post{ nil }.should be_true
15
+ end
16
+
17
+ it 'calls the block with the given arguments' do
18
+ @expected = nil
19
+ subject.post(1, 2, 3) do |a, b, c|
20
+ @expected = a + b + c
21
+ end
22
+ sleep(0.1)
23
+ @expected.should eq 6
24
+ end
25
+
26
+ it 'aliases #<<' do
27
+ @expected = false
28
+ subject << proc { @expected = true }
29
+ sleep(0.1)
30
+ @expected.should be_true
31
+ end
32
+ end
33
+ end
@@ -1,33 +1,12 @@
1
1
  require 'spec_helper'
2
+ require_relative 'global_thread_pool_shared'
2
3
 
3
4
  module Concurrent
4
5
 
5
6
  describe ImmediateExecutor do
6
7
 
7
- let(:executor) { ImmediateExecutor.new }
8
+ subject { ImmediateExecutor.new }
8
9
 
9
- context "#post" do
10
- it 'executes the block using the arguments as parameters' do
11
- result = executor.post(1, 2, 3, 4) { |a, b, c, d| [a, b, c, d] }
12
- result.should eq [1, 2, 3, 4]
13
- end
14
- end
15
-
16
- context "#<<" do
17
-
18
- it "returns true" do
19
- result = executor << proc { false }
20
- result.should be_true
21
- end
22
-
23
- it "executes the passed callable" do
24
- x = 0
25
-
26
- executor << proc { x = 5 }
27
-
28
- x.should eq 5
29
- end
30
-
31
- end
10
+ it_should_behave_like :global_thread_pool
32
11
  end
33
- end
12
+ end
@@ -1,14 +1,13 @@
1
1
  require 'spec_helper'
2
2
  require_relative 'dereferenceable_shared'
3
3
  require_relative 'obligation_shared'
4
- require_relative 'uses_global_thread_pool_shared'
5
4
 
6
5
  module Concurrent
7
6
 
8
7
  describe IVar do
9
8
 
10
9
  let!(:value) { 10 }
11
-
10
+
12
11
  subject do
13
12
  i = IVar.new
14
13
  i.set(14)
@@ -17,33 +16,33 @@ module Concurrent
17
16
 
18
17
  context 'behavior' do
19
18
 
20
- # obligation
19
+ # obligation
21
20
 
22
- let!(:fulfilled_value) { 10 }
23
- let(:rejected_reason) { StandardError.new('Boom!') }
21
+ let!(:fulfilled_value) { 10 }
22
+ let(:rejected_reason) { StandardError.new('Boom!') }
24
23
 
25
- let(:pending_subject) do
26
- @i = IVar.new
27
- Thread.new do
28
- sleep(3)
29
- @i.set(fulfilled_value)
24
+ let(:pending_subject) do
25
+ @i = IVar.new
26
+ Thread.new do
27
+ sleep(3)
28
+ @i.set(fulfilled_value)
29
+ end
30
+ @i
30
31
  end
31
- @i
32
- end
33
32
 
34
- let(:fulfilled_subject) do
35
- i = IVar.new
36
- i.set(fulfilled_value)
37
- i
38
- end
33
+ let(:fulfilled_subject) do
34
+ i = IVar.new
35
+ i.set(fulfilled_value)
36
+ i
37
+ end
39
38
 
40
- let(:rejected_subject) do
41
- i = IVar.new
42
- i.fail(rejected_reason)
43
- i
44
- end
39
+ let(:rejected_subject) do
40
+ i = IVar.new
41
+ i.fail(rejected_reason)
42
+ i
43
+ end
45
44
 
46
- it_should_behave_like :obligation
45
+ it_should_behave_like :obligation
47
46
 
48
47
  # dereferenceable
49
48
 
@@ -103,6 +102,10 @@ module Concurrent
103
102
  i.value.should eq 14
104
103
  end
105
104
 
105
+ it 'returns self' do
106
+ i = IVar.new
107
+ i.set(42).should eq i
108
+ end
106
109
  end
107
110
 
108
111
  context '#fail' do
@@ -126,6 +129,16 @@ module Concurrent
126
129
  i.value.should be_nil
127
130
  end
128
131
 
132
+ it 'defaults the reason to a StandardError' do
133
+ i = IVar.new
134
+ i.fail
135
+ i.reason.should be_a StandardError
136
+ end
137
+
138
+ it 'returns self' do
139
+ i = IVar.new
140
+ i.fail.should eq i
141
+ end
129
142
  end
130
143
 
131
144
  context 'observation' do
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ if jruby?
4
+
5
+ require_relative 'cached_thread_pool_shared'
6
+
7
+ module Concurrent
8
+
9
+ describe JavaCachedThreadPool do
10
+
11
+ subject { described_class.new(max_threads: 5, overflow_policy: :discard) }
12
+
13
+ after(:each) do
14
+ subject.kill
15
+ sleep(0.1)
16
+ end
17
+
18
+ it_should_behave_like :cached_thread_pool
19
+
20
+ context '#initialize' do
21
+
22
+ it 'sets :min_length correctly' do
23
+ subject = JavaCachedThreadPool.new
24
+ subject.min_length.should eq 0
25
+ end
26
+
27
+ it 'sets :max_length correctly' do
28
+ subject = JavaCachedThreadPool.new(max_threads: 5)
29
+ subject.max_length.should eq 5
30
+ end
31
+
32
+ it 'sets :idletime correctly' do
33
+ subject = JavaCachedThreadPool.new(idletime: 10)
34
+ subject.idletime.should eq 10
35
+ end
36
+
37
+ it 'sets :max_queue correctly' do
38
+ subject = JavaCachedThreadPool.new
39
+ subject.max_queue.should eq 0
40
+ end
41
+
42
+ it 'sets :overflow_policy correctly' do
43
+ clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy
44
+ policy = clazz.new
45
+ clazz.should_receive(:new).at_least(:once).with(any_args).and_return(policy)
46
+
47
+ subject = JavaCachedThreadPool.new(overflow_policy: :discard)
48
+ subject.overflow_policy.should eq :discard
49
+ end
50
+
51
+ it 'defaults :overflow_policy to :abort' do
52
+ subject = JavaCachedThreadPool.new
53
+ subject.overflow_policy.should eq :abort
54
+ end
55
+
56
+ it 'raises an exception if given an invalid :overflow_policy' do
57
+ expect {
58
+ JavaCachedThreadPool.new(overflow_policy: :bogus)
59
+ }.to raise_error(ArgumentError)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ if jruby?
4
+
5
+ require_relative 'fixed_thread_pool_shared'
6
+
7
+ module Concurrent
8
+
9
+ describe JavaFixedThreadPool do
10
+
11
+ subject { described_class.new(5, overflow_policy: :discard) }
12
+
13
+ after(:each) do
14
+ subject.kill
15
+ sleep(0.1)
16
+ end
17
+
18
+ it_should_behave_like :fixed_thread_pool
19
+
20
+ context '#initialize' do
21
+
22
+ it 'sets :min_length correctly' do
23
+ subject = JavaFixedThreadPool.new(10)
24
+ subject.min_length.should eq 10
25
+ end
26
+
27
+ it 'sets :max_length correctly' do
28
+ subject = JavaFixedThreadPool.new(5)
29
+ subject.max_length.should eq 5
30
+ end
31
+
32
+ it 'sets :idletime correctly' do
33
+ subject = JavaFixedThreadPool.new(5)
34
+ subject.idletime.should eq 0
35
+ end
36
+
37
+ it 'sets :max_queue correctly' do
38
+ subject = JavaFixedThreadPool.new(5)
39
+ subject.max_queue.should eq 0
40
+ end
41
+
42
+ it 'sets :overflow_policy correctly' do
43
+ clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy
44
+ policy = clazz.new
45
+ clazz.should_receive(:new).at_least(:once).with(any_args).and_return(policy)
46
+
47
+ subject = JavaFixedThreadPool.new(5, overflow_policy: :discard)
48
+ subject.overflow_policy.should eq :discard
49
+ end
50
+
51
+ it 'defaults :overflow_policy to :abort' do
52
+ subject = JavaFixedThreadPool.new(5)
53
+ subject.overflow_policy.should eq :abort
54
+ end
55
+
56
+ it 'raises an exception if given an invalid :overflow_policy' do
57
+ expect {
58
+ JavaFixedThreadPool.new(5, overflow_policy: :bogus)
59
+ }.to raise_error(ArgumentError)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ if jruby?
4
+
5
+ require_relative 'thread_pool_executor_shared'
6
+
7
+ module Concurrent
8
+
9
+ describe JavaThreadPoolExecutor do
10
+
11
+ after(:each) do
12
+ subject.kill
13
+ sleep(0.1)
14
+ end
15
+
16
+ subject do
17
+ JavaThreadPoolExecutor.new(
18
+ min_threads: 2,
19
+ max_threads: 5,
20
+ idletime: 60,
21
+ max_queue: 10,
22
+ overflow_policy: :discard
23
+ )
24
+ end
25
+
26
+ it_should_behave_like :thread_pool_executor
27
+
28
+ context '#overload_policy' do
29
+
30
+ specify ':abort maps to AbortPolicy' do
31
+ clazz = java.util.concurrent.ThreadPoolExecutor::AbortPolicy
32
+ policy = clazz.new
33
+ clazz.should_receive(:new).at_least(:once).with(any_args).and_return(policy)
34
+ JavaThreadPoolExecutor.new(
35
+ min_threads: 2,
36
+ max_threads: 5,
37
+ idletime: 60,
38
+ max_queue: 10,
39
+ overflow_policy: :abort
40
+ )
41
+ end
42
+
43
+ specify ':discard maps to DiscardPolicy' do
44
+ clazz = java.util.concurrent.ThreadPoolExecutor::DiscardPolicy
45
+ policy = clazz.new
46
+ clazz.should_receive(:new).at_least(:once).with(any_args).and_return(policy)
47
+ JavaThreadPoolExecutor.new(
48
+ min_threads: 2,
49
+ max_threads: 5,
50
+ idletime: 60,
51
+ max_queue: 10,
52
+ overflow_policy: :discard
53
+ )
54
+ end
55
+
56
+ specify ':caller_runs maps to CallerRunsPolicy' do
57
+ clazz = java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy
58
+ policy = clazz.new
59
+ clazz.should_receive(:new).at_least(:once).with(any_args).and_return(policy)
60
+ JavaThreadPoolExecutor.new(
61
+ min_threads: 2,
62
+ max_threads: 5,
63
+ idletime: 60,
64
+ max_queue: 10,
65
+ overflow_policy: :caller_runs
66
+ )
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -25,40 +25,52 @@ share_examples_for :obligation do
25
25
 
26
26
  context '#value' do
27
27
 
28
- it 'blocks the caller when :pending and timeout is nil' do
29
- f = pending_subject
30
- f.value.should be_true
31
- f.should be_fulfilled
32
- end
28
+ let!(:supports_timeout) { pending_subject.method(:value).arity != 0 }
33
29
 
34
30
  it 'returns nil when reaching the optional timeout value' do
35
- f = pending_subject
36
- f.value(0).should be_nil
37
- f.should be_pending
31
+ if supports_timeout
32
+ f = pending_subject
33
+ f.value(0).should be_nil
34
+ f.should be_pending
35
+ end
38
36
  end
39
37
 
40
38
  it 'returns immediately when timeout is zero' do
41
- Concurrent.should_not_receive(:timeout).with(any_args())
42
- f = pending_subject
43
- f.value(0).should be_nil
44
- f.should be_pending
39
+ if supports_timeout
40
+ Concurrent.should_not_receive(:timeout).with(any_args())
41
+ f = pending_subject
42
+ f.value(0).should be_nil
43
+ f.should be_pending
44
+ end
45
45
  end
46
46
 
47
47
  it 'returns the value when fulfilled before timeout' do
48
- f = pending_subject
49
- f.value(10).should be_true
50
- f.should be_fulfilled
48
+ if supports_timeout
49
+ f = pending_subject
50
+ f.value(10).should be_true
51
+ f.should be_fulfilled
52
+ end
51
53
  end
52
54
 
53
55
  it 'returns nil when timeout reached' do
54
- f = pending_subject
55
- f.value(0.1).should be_nil
56
- f.should be_pending
56
+ if supports_timeout
57
+ f = pending_subject
58
+ f.value(0.1).should be_nil
59
+ f.should be_pending
60
+ end
57
61
  end
58
62
 
59
63
  it 'is nil when :pending' do
60
- expected = pending_subject.value(0)
61
- expected.should be_nil
64
+ if supports_timeout
65
+ expected = pending_subject.value(0)
66
+ expected.should be_nil
67
+ end
68
+ end
69
+
70
+ it 'blocks the caller when :pending and timeout is nil' do
71
+ f = pending_subject
72
+ f.value.should be_true
73
+ f.should be_fulfilled
62
74
  end
63
75
 
64
76
  it 'is nil when :rejected' do
@@ -1,28 +1,24 @@
1
1
  require 'spec_helper'
2
- require_relative 'uses_global_thread_pool_shared'
2
+ require_relative 'global_thread_pool_shared'
3
3
 
4
4
  module Concurrent
5
5
 
6
- describe UsesGlobalThreadPool do
6
+ describe PerThreadExecutor do
7
7
 
8
- let!(:thread_pool_user){ Class.new{ include UsesGlobalThreadPool } }
9
- it_should_behave_like Concurrent::UsesGlobalThreadPool
10
- end
11
-
12
- describe NullThreadPool do
13
-
14
- subject { NullThreadPool.new }
8
+ subject { PerThreadExecutor.new }
15
9
 
16
10
  after(:all) do
17
- $GLOBAL_THREAD_POOL = FixedThreadPool.new(1)
11
+ Concurrent.configuration.global_task_pool = PerThreadExecutor.new
18
12
  end
19
13
 
14
+ it_should_behave_like :global_thread_pool
15
+
20
16
  context '#post' do
21
17
 
22
18
  it 'creates a new thread for a call without arguments' do
23
19
  thread = Thread.new{ nil }
24
20
  Thread.should_receive(:new).with(no_args()).and_return(thread)
25
- $GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
21
+ Concurrent.configuration.global_task_pool.should_not_receive(:post).with(any_args())
26
22
  subject.post{ nil }
27
23
  end
28
24
 
@@ -36,7 +32,7 @@ module Concurrent
36
32
  it 'creates a new thread for a call with arguments' do
37
33
  thread = Thread.new{ nil }
38
34
  Thread.should_receive(:new).with(1,2,3).and_return(thread)
39
- $GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
35
+ Concurrent.configuration.global_task_pool.should_not_receive(:post).with(any_args())
40
36
  subject.post(1,2,3){ nil }
41
37
  end
42
38
 
@@ -57,7 +53,7 @@ module Concurrent
57
53
  it 'aliases #<<' do
58
54
  thread = Thread.new{ nil }
59
55
  Thread.should_receive(:new).with(no_args()).and_return(thread)
60
- $GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
56
+ Concurrent.configuration.global_task_pool.should_not_receive(:post).with(any_args())
61
57
  subject << proc{ nil }
62
58
  end
63
59
  end