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
@@ -1,63 +0,0 @@
1
- require 'concurrent/actor'
2
- require 'concurrent/stoppable'
3
-
4
- module Concurrent
5
-
6
- # +Channel+ is a functional programming variation of +Actor+, based very loosely on the
7
- # *MailboxProcessor* agent in F#. +Actor+ is used to create objects that receive messages
8
- # from other threads then processes those messages based on the behavior of the class.
9
- # +Channel+ creates objects that receive messages and processe them using the block
10
- # given at construction. +Channel+ is implemented as a subclass of +Actor+ and supports
11
- # all message-passing methods of that class. +Channel+ also supports pools with a shared
12
- # mailbox.
13
- #
14
- # @example Basic usage
15
- # channel = Concurrent::Channel.new do |msg|
16
- # sleep(1)
17
- # puts "#{msg}\n"
18
- # end
19
- #
20
- # channel.run! => #<Thread:0x007fa123d95fc8 sleep>
21
- #
22
- # channel.post("Hello, World!") => 1
23
- # # wait...
24
- # => Hello, World!
25
- #
26
- # future = channel.post? "Don't Panic." => #<Concurrent::IVar:0x007fa123d6d9d8 @state=:pending...
27
- # future.pending? => true
28
- # # wait...
29
- # => "Don't Panic."
30
- # future.fulfilled? => true
31
- #
32
- # channel.stop => true
33
- #
34
- # @see http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx Async and Parallel Design Patterns in F#: Agents
35
- # @see http://msdn.microsoft.com/en-us/library/ee370357.aspx Control.MailboxProcessor<'Msg> Class (F#)
36
- class Channel < Actor
37
- include Stoppable
38
-
39
- # Initialize a new object with a block operation to be performed in response
40
- # to every received message.
41
- #
42
- # @yield [message] Removes the next message from the queue and processes it
43
- # @yieldparam [Array] msg The next message post to the channel
44
- def initialize(&block)
45
- raise ArgumentError.new('no block given') unless block_given?
46
- super()
47
- @task = block
48
- end
49
-
50
- protected
51
-
52
- def on_stop # :nodoc:
53
- before_stop_proc.call if before_stop_proc
54
- super
55
- end
56
-
57
- private
58
-
59
- def act(*message) # :nodoc:
60
- return @task.call(*message)
61
- end
62
- end
63
- end
@@ -1,54 +0,0 @@
1
- require 'thread'
2
-
3
- module Concurrent
4
-
5
- class FixedThreadPool
6
-
7
- class Worker
8
-
9
- def initialize(queue, parent)
10
- @queue = queue
11
- @parent = parent
12
- @mutex = Mutex.new
13
- end
14
-
15
- def dead?
16
- return @mutex.synchronize do
17
- @thread.nil? ? false : ! @thread.alive?
18
- end
19
- end
20
-
21
- def kill
22
- @mutex.synchronize do
23
- Thread.kill(@thread) unless @thread.nil?
24
- @thread = nil
25
- end
26
- end
27
-
28
- def run(thread = Thread.current)
29
- @mutex.synchronize do
30
- raise StandardError.new('already running') unless @thread.nil?
31
- @thread = thread
32
- end
33
-
34
- loop do
35
- task = @queue.pop
36
- if task == :stop
37
- @thread = nil
38
- @parent.on_worker_exit(self)
39
- break
40
- end
41
-
42
- @parent.on_start_task(self)
43
- begin
44
- task.last.call(*task.first)
45
- rescue
46
- # let it fail
47
- ensure
48
- @parent.on_end_task(self)
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
@@ -1,42 +0,0 @@
1
- module Concurrent
2
-
3
- class NullThreadPool
4
-
5
- def self.post(*args)
6
- Thread.new(*args) do
7
- Thread.current.abort_on_exception = false
8
- yield(*args)
9
- end
10
- return true
11
- end
12
-
13
- def post(*args, &block)
14
- return NullThreadPool.post(*args, &block)
15
- end
16
-
17
- def <<(block)
18
- NullThreadPool.post(&block)
19
- return self
20
- end
21
- end
22
-
23
- module UsesGlobalThreadPool
24
-
25
- def self.included(base)
26
- class << base
27
- def thread_pool
28
- @thread_pool || $GLOBAL_THREAD_POOL
29
- end
30
- def thread_pool=(pool)
31
- if pool == $GLOBAL_THREAD_POOL
32
- @thread_pool = nil
33
- else
34
- @thread_pool = pool
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
41
-
42
- $GLOBAL_THREAD_POOL ||= Concurrent::NullThreadPool.new
@@ -1,101 +0,0 @@
1
- require 'spec_helper'
2
- require_relative 'thread_pool_shared'
3
-
4
- module Concurrent
5
-
6
- describe CachedThreadPool do
7
-
8
- subject { CachedThreadPool.new(max_threads: 5) }
9
-
10
- after(:each) do
11
- subject.kill
12
- sleep(0.1)
13
- end
14
-
15
- it_should_behave_like :thread_pool
16
-
17
- context '#initialize' do
18
-
19
- it 'raises an exception when the pool size is less than one' do
20
- lambda {
21
- CachedThreadPool.new(max: 0)
22
- }.should raise_error(ArgumentError)
23
- end
24
-
25
- it 'raises an exception when the pool size is greater than MAX_POOL_SIZE' do
26
- lambda {
27
- CachedThreadPool.new(max: CachedThreadPool::MAX_POOL_SIZE + 1)
28
- }.should raise_error(ArgumentError)
29
- end
30
- end
31
-
32
- context '#length' do
33
-
34
- it 'returns zero for a new thread pool' do
35
- subject.length.should eq 0
36
- end
37
-
38
- it 'returns the length of the subject when running' do
39
- 5.times{ sleep(0.1); subject << proc{ sleep(1) } }
40
- subject.length.should eq 5
41
- end
42
-
43
- it 'returns zero once shut down' do
44
- subject.shutdown
45
- subject.length.should eq 0
46
- end
47
- end
48
-
49
- context 'worker creation and caching' do
50
-
51
- it 'creates new workers when there are none available' do
52
- subject.length.should eq 0
53
- 5.times{ sleep(0.1); subject << proc{ sleep } }
54
- sleep(1)
55
- subject.length.should eq 5
56
- end
57
-
58
- it 'uses existing idle threads' do
59
- 5.times{ subject << proc{ sleep(0.1) } }
60
- sleep(1)
61
- subject.length.should >= 5
62
- 3.times{ subject << proc{ sleep } }
63
- sleep(0.1)
64
- subject.length.should >= 5
65
- end
66
-
67
- it 'never creates more than :max_threads threads' do
68
- pool = CachedThreadPool.new(max: 5)
69
- 100.times{ sleep(0.01); pool << proc{ sleep } }
70
- sleep(0.1)
71
- pool.length.should eq 5
72
- pool.kill
73
- end
74
-
75
- it 'sets :max_threads to MAX_POOL_SIZE when not given' do
76
- CachedThreadPool.new.max_threads.should eq CachedThreadPool::MAX_POOL_SIZE
77
- end
78
- end
79
-
80
- context 'garbage collection' do
81
-
82
- subject{ CachedThreadPool.new(gc_interval: 1, idletime: 1) }
83
-
84
- it 'removes from pool any thread that has been idle too long' do
85
- 3.times { subject << proc{ sleep(0.1) } }
86
- subject.length.should eq 3
87
- sleep(2)
88
- subject << proc{ nil }
89
- subject.length.should < 3
90
- end
91
-
92
- it 'removed from pool any dead thread' do
93
- 3.times { subject << proc{ sleep(0.1); raise Exception } }
94
- subject.length.should == 3
95
- sleep(2)
96
- subject << proc{ nil }
97
- subject.length.should < 3
98
- end
99
- end
100
- end
101
- end
@@ -1,86 +0,0 @@
1
- require 'spec_helper'
2
- require_relative 'postable_shared'
3
- require_relative 'runnable_shared'
4
- require_relative 'stoppable_shared'
5
-
6
- module Concurrent
7
-
8
- describe Channel do
9
-
10
- context :runnable do
11
- subject{ Channel.new{ nil } }
12
- it_should_behave_like :runnable
13
- end
14
-
15
- context :stoppable do
16
- subject do
17
- task = Channel.new{ nil }
18
- task.run!
19
- task
20
- end
21
- it_should_behave_like :stoppable
22
- end
23
-
24
- context :postable do
25
-
26
- let!(:postable_class){ Channel }
27
-
28
- let(:sender) do
29
- Channel.new do |*message|
30
- if message.first.is_a?(Exception)
31
- raise message.first
32
- else
33
- message.first
34
- end
35
- end
36
- end
37
-
38
- let(:receiver){ Channel.new{|*message| message.first } }
39
-
40
- it_should_behave_like :postable
41
- end
42
-
43
- subject{ Channel.new{ nil } }
44
-
45
- context '#initialize' do
46
-
47
- it 'raises an exception if no block is given' do
48
- expect {
49
- Channel.new
50
- }.to raise_error(ArgumentError)
51
- end
52
- end
53
-
54
- context '#behavior' do
55
-
56
- it 'calls the block once for each message' do
57
- @expected = false
58
- channel = Channel.new{ @expected = true }
59
- channel.run!
60
- channel << 42
61
- sleep(0.1)
62
- channel.stop
63
- @expected.should be_true
64
- end
65
-
66
- it 'passes all arguments to the block' do
67
- @expected = []
68
- channel = Channel.new{|*message| @expected = message }
69
- channel.run!
70
- channel.post(1,2,3,4,5)
71
- sleep(0.1)
72
- channel.stop
73
- @expected.should eq [1,2,3,4,5]
74
- end
75
- end
76
-
77
- context '#pool' do
78
-
79
- it 'passes a duplicate of the block to each channel in the pool' do
80
- block = proc{ nil }
81
- block.should_receive(:dup).exactly(5).times.and_return(proc{ nil })
82
- mbox, pool = Channel.pool(5, &block)
83
- end
84
- end
85
- end
86
- end
@@ -1,92 +0,0 @@
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
- after(:each) do
11
- subject.kill
12
- sleep(0.1)
13
- end
14
-
15
- it_should_behave_like :thread_pool
16
-
17
- context '#initialize' do
18
-
19
- it 'raises an exception when the pool length is less than one' do
20
- lambda {
21
- FixedThreadPool.new(0)
22
- }.should raise_error(ArgumentError)
23
- end
24
-
25
- it 'raises an exception when the pool length is greater than MAX_POOL_SIZE' do
26
- lambda {
27
- FixedThreadPool.new(FixedThreadPool::MAX_POOL_SIZE + 1)
28
- }.should raise_error(ArgumentError)
29
- end
30
- end
31
-
32
- context '#length' do
33
-
34
- let(:pool_length) { 3 }
35
- subject { FixedThreadPool.new(pool_length) }
36
-
37
- it 'returns zero on start' do
38
- subject.shutdown
39
- subject.length.should eq 0
40
- end
41
-
42
- it 'returns the length of the pool when running' do
43
- pool_length.times do |i|
44
- subject.post{ sleep }
45
- sleep(0.1)
46
- subject.length.should eq pool_length
47
- end
48
- end
49
-
50
- it 'returns zero while shutting down' do
51
- subject.post{ sleep(1) }
52
- subject.shutdown
53
- subject.length.should eq 0
54
- end
55
-
56
- it 'returns zero once shut down' do
57
- subject.shutdown
58
- subject.length.should eq 0
59
- end
60
- end
61
-
62
- context 'worker creation and caching' do
63
-
64
- it 'creates new workers when there are none available' do
65
- pool = FixedThreadPool.new(5)
66
- pool.length.should eq 0
67
- 5.times{ pool << proc{ sleep } }
68
- sleep(0.1)
69
- pool.length.should eq 5
70
- pool.kill
71
- end
72
-
73
- it 'never creates more than :max_threads threads' do
74
- pool = FixedThreadPool.new(5)
75
- 100.times{ pool << proc{ sleep } }
76
- sleep(0.1)
77
- pool.length.should eq 5
78
- pool.kill
79
- end
80
- end
81
-
82
- context 'exception handling' do
83
-
84
- it 'restarts threads that experience exception' do
85
- pool = FixedThreadPool.new(5)
86
- 5.times{ pool << proc{ raise StandardError } }
87
- sleep(1)
88
- pool.length.should eq 5
89
- end
90
- end
91
- end
92
- end
@@ -1,64 +0,0 @@
1
- require 'spec_helper'
2
-
3
- share_examples_for Concurrent::UsesGlobalThreadPool do
4
-
5
- before(:each) do
6
- $GLOBAL_THREAD_POOL = Concurrent::FixedThreadPool.new(1)
7
- end
8
-
9
- it 'defaults to the global thread pool' do
10
- clazz = Class.new(thread_pool_user)
11
- clazz.thread_pool.should eq $GLOBAL_THREAD_POOL
12
- end
13
-
14
- it 'sets and gets the thread pool for the class' do
15
- pool = Concurrent::NullThreadPool.new
16
- thread_pool_user.thread_pool = pool
17
- thread_pool_user.thread_pool.should eq pool
18
- end
19
-
20
- it 'gives each class its own thread pool' do
21
- subject1 = Class.new(thread_pool_user){ include Concurrent::UsesGlobalThreadPool }
22
- subject2 = Class.new(thread_pool_user){ include Concurrent::UsesGlobalThreadPool }
23
- subject3 = Class.new(thread_pool_user){ include Concurrent::UsesGlobalThreadPool }
24
-
25
- subject1.thread_pool = Concurrent::FixedThreadPool.new(1)
26
- subject2.thread_pool = Concurrent::CachedThreadPool.new
27
- subject3.thread_pool = Concurrent::NullThreadPool.new
28
-
29
- subject1.thread_pool.should_not eq subject2.thread_pool
30
- subject2.thread_pool.should_not eq subject3.thread_pool
31
- subject3.thread_pool.should_not eq subject1.thread_pool
32
- end
33
-
34
- it 'uses the new global thread pool after the global thread pool is changed' do
35
- null_thread_pool = Concurrent::NullThreadPool.new
36
- thread_pool_user.thread_pool = $GLOBAL_THREAD_POOL
37
-
38
- thread_pool_user.thread_pool.should eq $GLOBAL_THREAD_POOL
39
- thread_pool_user.thread_pool.should_not eq null_thread_pool
40
-
41
- $GLOBAL_THREAD_POOL = null_thread_pool
42
-
43
- thread_pool_user.thread_pool.should eq $GLOBAL_THREAD_POOL
44
- thread_pool_user.thread_pool.should eq null_thread_pool
45
- end
46
-
47
- it 'responds to multiple changes in the global thread pool' do
48
- thread_pool_user.thread_pool = $GLOBAL_THREAD_POOL
49
- thread_pool_user.thread_pool.should eq $GLOBAL_THREAD_POOL
50
-
51
- thread_pool_user.thread_pool = Concurrent::NullThreadPool.new
52
- thread_pool_user.thread_pool.should_not eq $GLOBAL_THREAD_POOL
53
-
54
- $GLOBAL_THREAD_POOL = Concurrent::FixedThreadPool.new(1)
55
- thread_pool_user.thread_pool = $GLOBAL_THREAD_POOL
56
- thread_pool_user.thread_pool.should eq $GLOBAL_THREAD_POOL
57
-
58
- $GLOBAL_THREAD_POOL = Concurrent::CachedThreadPool.new
59
- thread_pool_user.thread_pool.should eq $GLOBAL_THREAD_POOL
60
-
61
- $GLOBAL_THREAD_POOL = Concurrent::NullThreadPool.new
62
- thread_pool_user.thread_pool.should eq $GLOBAL_THREAD_POOL
63
- end
64
- end