concurrent-ruby 0.7.0.rc0-x86-linux
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.
- checksums.yaml +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
- data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
- data/ext/concurrent_ruby_ext/extconf.rb +59 -0
- data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
- data/lib/concurrent.rb +45 -0
- data/lib/concurrent/actress.rb +221 -0
- data/lib/concurrent/actress/ad_hoc.rb +20 -0
- data/lib/concurrent/actress/context.rb +98 -0
- data/lib/concurrent/actress/core.rb +228 -0
- data/lib/concurrent/actress/core_delegations.rb +42 -0
- data/lib/concurrent/actress/envelope.rb +41 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +232 -0
- data/lib/concurrent/async.rb +319 -0
- data/lib/concurrent/atomic.rb +46 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
- data/lib/concurrent/atomic/condition.rb +67 -0
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +98 -0
- data/lib/concurrent/atomic/thread_local_var.rb +117 -0
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
- data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
- data/lib/concurrent/atomic_reference/jruby.rb +8 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
- data/lib/concurrent/atomic_reference/rbx.rb +16 -0
- data/lib/concurrent/atomic_reference/ruby.rb +16 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/channel/buffered_channel.rb +85 -0
- data/lib/concurrent/channel/channel.rb +41 -0
- data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
- data/lib/concurrent/channel/waitable_list.rb +40 -0
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/collection/ring_buffer.rb +59 -0
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +158 -0
- data/lib/concurrent/dataflow.rb +91 -0
- data/lib/concurrent/delay.rb +112 -0
- data/lib/concurrent/dereferenceable.rb +101 -0
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/exchanger.rb +34 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
- data/lib/concurrent/executor/executor.rb +229 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
- data/lib/concurrent/executor/immediate_executor.rb +16 -0
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
- data/lib/concurrent/executor/per_thread_executor.rb +24 -0
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serialized_execution.rb +90 -0
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +143 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +124 -0
- data/lib/concurrent/ivar.rb +111 -0
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +200 -0
- data/lib/concurrent/obligation.rb +171 -0
- data/lib/concurrent/observable.rb +40 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +169 -0
- data/lib/concurrent/scheduled_task.rb +78 -0
- data/lib/concurrent/supervisor.rb +343 -0
- data/lib/concurrent/timer_task.rb +341 -0
- data/lib/concurrent/tvar.rb +252 -0
- data/lib/concurrent/utilities.rb +3 -0
- data/lib/concurrent/utility/processor_count.rb +150 -0
- data/lib/concurrent/utility/timeout.rb +35 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +3 -0
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +140 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'concurrent/atomic_reference/concurrent_update_error'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# Define update methods that delegate to @ref field
|
6
|
+
class Atomic
|
7
|
+
# Pass the current value to the given block, replacing it
|
8
|
+
# with the block's result. May retry if the value changes
|
9
|
+
# during the block's execution.
|
10
|
+
def update
|
11
|
+
true until @ref.compare_and_set(old_value = @ref.get, new_value = yield(old_value))
|
12
|
+
new_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def try_update
|
16
|
+
old_value = @ref.get
|
17
|
+
new_value = yield old_value
|
18
|
+
unless @ref.compare_and_set(old_value, new_value)
|
19
|
+
if $VERBOSE
|
20
|
+
raise ConcurrentUpdateError, "Update failed"
|
21
|
+
else
|
22
|
+
raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
new_value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'concurrent/atomic_reference/concurrent_update_error'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# Define update methods that use direct paths
|
6
|
+
module AtomicDirectUpdate
|
7
|
+
# Pass the current value to the given block, replacing it
|
8
|
+
# with the block's result. May retry if the value changes
|
9
|
+
# during the block's execution.
|
10
|
+
def update
|
11
|
+
true until compare_and_set(old_value = get, new_value = yield(old_value))
|
12
|
+
new_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def try_update
|
16
|
+
old_value = get
|
17
|
+
new_value = yield old_value
|
18
|
+
unless compare_and_set(old_value, new_value)
|
19
|
+
if $VERBOSE
|
20
|
+
raise ConcurrentUpdateError, "Update failed"
|
21
|
+
else
|
22
|
+
raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
new_value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'concurrent/atomic_reference/direct_update'
|
3
|
+
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
|
7
|
+
# Portable/generic (but not very memory or scheduling-efficient) fallback
|
8
|
+
class MutexAtomic #:nodoc: all
|
9
|
+
include Concurrent::AtomicDirectUpdate
|
10
|
+
include Concurrent::AtomicNumericCompareAndSetWrapper
|
11
|
+
|
12
|
+
def initialize(value = nil)
|
13
|
+
@mutex = Mutex.new
|
14
|
+
@value = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def get
|
18
|
+
@mutex.synchronize { @value }
|
19
|
+
end
|
20
|
+
alias value get
|
21
|
+
|
22
|
+
def set(new_value)
|
23
|
+
@mutex.synchronize { @value = new_value }
|
24
|
+
end
|
25
|
+
alias value= set
|
26
|
+
|
27
|
+
def get_and_set(new_value)
|
28
|
+
@mutex.synchronize do
|
29
|
+
old_value = @value
|
30
|
+
@value = new_value
|
31
|
+
old_value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias swap get_and_set
|
35
|
+
|
36
|
+
def _compare_and_set(old_value, new_value)
|
37
|
+
return false unless @mutex.try_lock
|
38
|
+
begin
|
39
|
+
return false unless @value.equal? old_value
|
40
|
+
@value = new_value
|
41
|
+
ensure
|
42
|
+
@mutex.unlock
|
43
|
+
end
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
module AtomicNumericCompareAndSetWrapper
|
4
|
+
#alias _compare_and_set compare_and_set
|
5
|
+
|
6
|
+
def compare_and_set(expected, new)
|
7
|
+
if expected.kind_of? Numeric
|
8
|
+
while true
|
9
|
+
old = get
|
10
|
+
|
11
|
+
return false unless old.kind_of? Numeric
|
12
|
+
|
13
|
+
return false unless old == expected
|
14
|
+
|
15
|
+
result = _compare_and_set(old, new)
|
16
|
+
return result if result
|
17
|
+
end
|
18
|
+
else
|
19
|
+
_compare_and_set(expected, new)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias compare_and_swap compare_and_set
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'concurrent/atomic_reference/direct_update'
|
2
|
+
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# extend Rubinius's version adding aliases and numeric logic
|
7
|
+
class RbxAtomic < Rubinius::AtomicReference
|
8
|
+
alias _compare_and_set compare_and_set
|
9
|
+
include Concurrent::AtomicDirectUpdate
|
10
|
+
include Concurrent::AtomicNumericCompareAndSetWrapper
|
11
|
+
|
12
|
+
alias value get
|
13
|
+
alias value= set
|
14
|
+
alias swap get_and_set
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
begin
|
2
|
+
require 'concurrent_ruby_ext'
|
3
|
+
rescue LoadError
|
4
|
+
# may be a Windows cross-compiled native gem
|
5
|
+
require "#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'concurrent/atomic_reference/direct_update'
|
9
|
+
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
10
|
+
|
11
|
+
module Concurrent
|
12
|
+
class CAtomic
|
13
|
+
include Concurrent::AtomicDirectUpdate
|
14
|
+
include Concurrent::AtomicNumericCompareAndSetWrapper
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'concurrent/atomic'
|
2
|
+
require 'concurrent/atomic/atomic_boolean'
|
3
|
+
require 'concurrent/atomic/atomic_fixnum'
|
4
|
+
require 'concurrent/atomic/condition'
|
5
|
+
require 'concurrent/atomic/copy_on_notify_observer_set'
|
6
|
+
require 'concurrent/atomic/copy_on_write_observer_set'
|
7
|
+
require 'concurrent/atomic/cyclic_barrier'
|
8
|
+
require 'concurrent/atomic/count_down_latch'
|
9
|
+
require 'concurrent/atomic/event'
|
10
|
+
require 'concurrent/atomic/thread_local_var'
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'concurrent/atomic/condition'
|
2
|
+
|
3
|
+
require_relative 'waitable_list'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
class BufferedChannel
|
7
|
+
|
8
|
+
def initialize(size)
|
9
|
+
@mutex = Mutex.new
|
10
|
+
@condition = Condition.new
|
11
|
+
@buffer_condition = Condition.new
|
12
|
+
|
13
|
+
@probe_set = WaitableList.new
|
14
|
+
@buffer = RingBuffer.new(size)
|
15
|
+
end
|
16
|
+
|
17
|
+
def probe_set_size
|
18
|
+
@probe_set.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def buffer_queue_size
|
22
|
+
@mutex.synchronize { @buffer.count }
|
23
|
+
end
|
24
|
+
|
25
|
+
def push(value)
|
26
|
+
until set_probe_or_push_into_buffer(value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def pop
|
31
|
+
probe = Channel::Probe.new
|
32
|
+
select(probe)
|
33
|
+
probe.value
|
34
|
+
end
|
35
|
+
|
36
|
+
def select(probe)
|
37
|
+
@mutex.synchronize do
|
38
|
+
|
39
|
+
if @buffer.empty?
|
40
|
+
@probe_set.put(probe)
|
41
|
+
true
|
42
|
+
else
|
43
|
+
shift_buffer if probe.set_unless_assigned(peek_buffer, self)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove_probe(probe)
|
50
|
+
@probe_set.delete(probe)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def push_into_buffer(value)
|
56
|
+
@buffer_condition.wait(@mutex) while @buffer.full?
|
57
|
+
@buffer.offer value
|
58
|
+
@buffer_condition.broadcast
|
59
|
+
end
|
60
|
+
|
61
|
+
def peek_buffer
|
62
|
+
@buffer_condition.wait(@mutex) while @buffer.empty?
|
63
|
+
@buffer.peek
|
64
|
+
end
|
65
|
+
|
66
|
+
def shift_buffer
|
67
|
+
@buffer_condition.wait(@mutex) while @buffer.empty?
|
68
|
+
result = @buffer.poll
|
69
|
+
@buffer_condition.broadcast
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_probe_or_push_into_buffer(value)
|
74
|
+
@mutex.synchronize do
|
75
|
+
if @probe_set.empty?
|
76
|
+
push_into_buffer(value)
|
77
|
+
true
|
78
|
+
else
|
79
|
+
@probe_set.take.set_unless_assigned(value, self)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'concurrent/ivar'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Channel
|
5
|
+
|
6
|
+
class Probe < Concurrent::IVar
|
7
|
+
|
8
|
+
def initialize(value = NO_VALUE, opts = {})
|
9
|
+
super(value, opts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_unless_assigned(value, channel)
|
13
|
+
mutex.synchronize do
|
14
|
+
return false if [:fulfilled, :rejected].include? @state
|
15
|
+
|
16
|
+
set_state(true, [value, channel], nil)
|
17
|
+
event.set
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :composite_value, :value
|
23
|
+
|
24
|
+
def value
|
25
|
+
composite_value.nil? ? nil : composite_value[0]
|
26
|
+
end
|
27
|
+
|
28
|
+
def channel
|
29
|
+
composite_value.nil? ? nil : composite_value[1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.select(*channels)
|
34
|
+
probe = Probe.new
|
35
|
+
channels.each { |channel| channel.select(probe) }
|
36
|
+
result = probe.composite_value
|
37
|
+
channels.each { |channel| channel.remove_probe(probe) }
|
38
|
+
result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'waitable_list'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class UnbufferedChannel
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@probe_set = WaitableList.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def probe_set_size
|
11
|
+
@probe_set.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def push(value)
|
15
|
+
until @probe_set.take.set_unless_assigned(value, self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def pop
|
20
|
+
probe = Channel::Probe.new
|
21
|
+
select(probe)
|
22
|
+
probe.value
|
23
|
+
end
|
24
|
+
|
25
|
+
def select(probe)
|
26
|
+
@probe_set.put(probe)
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_probe(probe)
|
30
|
+
@probe_set.delete(probe)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'concurrent/atomic/condition'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class WaitableList
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@condition = Condition.new
|
9
|
+
|
10
|
+
@list = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def size
|
14
|
+
@mutex.synchronize { @list.size }
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
@mutex.synchronize { @list.empty? }
|
19
|
+
end
|
20
|
+
|
21
|
+
def put(value)
|
22
|
+
@mutex.synchronize do
|
23
|
+
@list << value
|
24
|
+
@condition.signal
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete(value)
|
29
|
+
@mutex.synchronize { @list.delete(value) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def take
|
33
|
+
@mutex.synchronize do
|
34
|
+
@condition.wait(@mutex) while @list.empty?
|
35
|
+
@list.shift
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'concurrent/atomic/condition'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class BlockingRingBuffer
|
5
|
+
|
6
|
+
def initialize(capacity)
|
7
|
+
@buffer = RingBuffer.new(capacity)
|
8
|
+
@first = @last = 0
|
9
|
+
@count = 0
|
10
|
+
@mutex = Mutex.new
|
11
|
+
@condition = Condition.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Integer] the capacity of the buffer
|
15
|
+
def capacity
|
16
|
+
@mutex.synchronize { @buffer.capacity }
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] the number of elements currently in the buffer
|
20
|
+
def count
|
21
|
+
@mutex.synchronize { @buffer.count }
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] true if buffer is empty, false otherwise
|
25
|
+
def empty?
|
26
|
+
@mutex.synchronize { @buffer.empty? }
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if buffer is full, false otherwise
|
30
|
+
def full?
|
31
|
+
@mutex.synchronize { @buffer.full? }
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [Object] value the value to be inserted
|
35
|
+
# @return [Boolean] true if value has been inserted, false otherwise
|
36
|
+
def put(value)
|
37
|
+
@mutex.synchronize do
|
38
|
+
wait_while_full
|
39
|
+
@buffer.offer(value)
|
40
|
+
@condition.signal
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Object] the first available value and removes it from the buffer. If buffer is empty it blocks until an element is available
|
46
|
+
def take
|
47
|
+
@mutex.synchronize do
|
48
|
+
wait_while_empty
|
49
|
+
result = @buffer.poll
|
50
|
+
@condition.signal
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Object] the first available value and without removing it from the buffer. If buffer is empty returns nil
|
56
|
+
def peek
|
57
|
+
@mutex.synchronize { @buffer.peek }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def wait_while_full
|
63
|
+
@condition.wait(@mutex) while @buffer.full?
|
64
|
+
end
|
65
|
+
|
66
|
+
def wait_while_empty
|
67
|
+
@condition.wait(@mutex) while @buffer.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|