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