concurrent-ruby 0.7.0.rc0-x86-mingw32

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 (96) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +166 -0
  4. data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
  5. data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
  6. data/ext/concurrent_ruby_ext/extconf.rb +59 -0
  7. data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
  8. data/lib/1.9/concurrent_ruby_ext.so +0 -0
  9. data/lib/2.0/concurrent_ruby_ext.so +0 -0
  10. data/lib/concurrent.rb +45 -0
  11. data/lib/concurrent/actress.rb +221 -0
  12. data/lib/concurrent/actress/ad_hoc.rb +20 -0
  13. data/lib/concurrent/actress/context.rb +98 -0
  14. data/lib/concurrent/actress/core.rb +228 -0
  15. data/lib/concurrent/actress/core_delegations.rb +42 -0
  16. data/lib/concurrent/actress/envelope.rb +41 -0
  17. data/lib/concurrent/actress/errors.rb +14 -0
  18. data/lib/concurrent/actress/reference.rb +64 -0
  19. data/lib/concurrent/actress/type_check.rb +48 -0
  20. data/lib/concurrent/agent.rb +232 -0
  21. data/lib/concurrent/async.rb +319 -0
  22. data/lib/concurrent/atomic.rb +46 -0
  23. data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
  24. data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
  25. data/lib/concurrent/atomic/condition.rb +67 -0
  26. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
  27. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
  28. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  29. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  30. data/lib/concurrent/atomic/event.rb +98 -0
  31. data/lib/concurrent/atomic/thread_local_var.rb +117 -0
  32. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  33. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  34. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  35. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  36. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  37. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  38. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  39. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  40. data/lib/concurrent/atomics.rb +10 -0
  41. data/lib/concurrent/channel/buffered_channel.rb +85 -0
  42. data/lib/concurrent/channel/channel.rb +41 -0
  43. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  44. data/lib/concurrent/channel/waitable_list.rb +40 -0
  45. data/lib/concurrent/channels.rb +5 -0
  46. data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
  47. data/lib/concurrent/collection/priority_queue.rb +305 -0
  48. data/lib/concurrent/collection/ring_buffer.rb +59 -0
  49. data/lib/concurrent/collections.rb +3 -0
  50. data/lib/concurrent/configuration.rb +158 -0
  51. data/lib/concurrent/dataflow.rb +91 -0
  52. data/lib/concurrent/delay.rb +112 -0
  53. data/lib/concurrent/dereferenceable.rb +101 -0
  54. data/lib/concurrent/errors.rb +30 -0
  55. data/lib/concurrent/exchanger.rb +34 -0
  56. data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
  57. data/lib/concurrent/executor/executor.rb +229 -0
  58. data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
  59. data/lib/concurrent/executor/immediate_executor.rb +16 -0
  60. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  61. data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
  62. data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
  63. data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
  64. data/lib/concurrent/executor/per_thread_executor.rb +24 -0
  65. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  66. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
  67. data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
  68. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
  69. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
  70. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  71. data/lib/concurrent/executor/serialized_execution.rb +90 -0
  72. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  73. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  74. data/lib/concurrent/executor/timer_set.rb +143 -0
  75. data/lib/concurrent/executors.rb +9 -0
  76. data/lib/concurrent/future.rb +124 -0
  77. data/lib/concurrent/ivar.rb +111 -0
  78. data/lib/concurrent/logging.rb +17 -0
  79. data/lib/concurrent/mvar.rb +200 -0
  80. data/lib/concurrent/obligation.rb +171 -0
  81. data/lib/concurrent/observable.rb +40 -0
  82. data/lib/concurrent/options_parser.rb +46 -0
  83. data/lib/concurrent/promise.rb +169 -0
  84. data/lib/concurrent/scheduled_task.rb +78 -0
  85. data/lib/concurrent/supervisor.rb +343 -0
  86. data/lib/concurrent/timer_task.rb +341 -0
  87. data/lib/concurrent/tvar.rb +252 -0
  88. data/lib/concurrent/utilities.rb +3 -0
  89. data/lib/concurrent/utility/processor_count.rb +150 -0
  90. data/lib/concurrent/utility/timeout.rb +35 -0
  91. data/lib/concurrent/utility/timer.rb +21 -0
  92. data/lib/concurrent/version.rb +3 -0
  93. data/lib/concurrent_ruby.rb +1 -0
  94. data/lib/concurrent_ruby_ext.so +0 -0
  95. data/lib/extension_helper.rb +9 -0
  96. metadata +142 -0
@@ -0,0 +1,117 @@
1
+ module Concurrent
2
+
3
+ # A thread safe observer set implemented using copy-on-write approach:
4
+ # every time an observer is added or removed the whole internal data structure is
5
+ # duplicated and replaced with a new one.
6
+ class CopyOnWriteObserverSet
7
+
8
+ def initialize
9
+ @mutex = Mutex.new
10
+ @observers = {}
11
+ end
12
+
13
+ # Adds an observer to this set
14
+ # If a block is passed, the observer will be created by this method and no other params should be passed
15
+ # @param [Object] observer the observer to add
16
+ # @param [Symbol] func the function to call on the observer during notification. Default is :update
17
+ # @return [Object] the added observer
18
+ def add_observer(observer=nil, func=:update, &block)
19
+ if observer.nil? && block.nil?
20
+ raise ArgumentError, 'should pass observer as a first argument or block'
21
+ elsif observer && block
22
+ raise ArgumentError.new('cannot provide both an observer and a block')
23
+ end
24
+
25
+ if block
26
+ observer = block
27
+ func = :call
28
+ end
29
+
30
+ begin
31
+ @mutex.lock
32
+ new_observers = @observers.dup
33
+ new_observers[observer] = func
34
+ @observers = new_observers
35
+ observer
36
+ ensure
37
+ @mutex.unlock
38
+ end
39
+ end
40
+
41
+ # @param [Object] observer the observer to remove
42
+ # @return [Object] the deleted observer
43
+ def delete_observer(observer)
44
+ @mutex.lock
45
+ new_observers = @observers.dup
46
+ new_observers.delete(observer)
47
+ @observers = new_observers
48
+ observer
49
+ ensure
50
+ @mutex.unlock
51
+ end
52
+
53
+ # Deletes all observers
54
+ # @return [CopyOnWriteObserverSet] self
55
+ def delete_observers
56
+ self.observers = {}
57
+ self
58
+ end
59
+
60
+
61
+ # @return [Integer] the observers count
62
+ def count_observers
63
+ observers.count
64
+ end
65
+
66
+ # Notifies all registered observers with optional args
67
+ # @param [Object] args arguments to be passed to each observer
68
+ # @return [CopyOnWriteObserverSet] self
69
+ def notify_observers(*args, &block)
70
+ notify_to(observers, *args, &block)
71
+ self
72
+ end
73
+
74
+ # Notifies all registered observers with optional args and deletes them.
75
+ #
76
+ # @param [Object] args arguments to be passed to each observer
77
+ # @return [CopyOnWriteObserverSet] self
78
+ def notify_and_delete_observers(*args, &block)
79
+ old = clear_observers_and_return_old
80
+ notify_to(old, *args, &block)
81
+ self
82
+ end
83
+
84
+ private
85
+
86
+ def notify_to(observers, *args)
87
+ raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
88
+ observers.each do |observer, function|
89
+ args = yield if block_given?
90
+ observer.send(function, *args)
91
+ end
92
+ end
93
+
94
+ def observers
95
+ @mutex.lock
96
+ @observers
97
+ ensure
98
+ @mutex.unlock
99
+ end
100
+
101
+ def observers=(new_set)
102
+ @mutex.lock
103
+ @observers = new_set
104
+ ensure
105
+ @mutex.unlock
106
+ end
107
+
108
+ def clear_observers_and_return_old
109
+ @mutex.lock
110
+ old_observers = @observers
111
+ @observers = {}
112
+ old_observers
113
+ ensure
114
+ @mutex.unlock
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,116 @@
1
+ require 'concurrent/atomic/condition'
2
+
3
+ module Concurrent
4
+
5
+ # @!macro [attach] count_down_latch
6
+ #
7
+ # A synchronization object that allows one thread to wait on multiple other threads.
8
+ # The thread that will wait creates a `CountDownLatch` and sets the initial value
9
+ # (normally equal to the number of other threads). The initiating thread passes the
10
+ # latch to the other threads then waits for the other threads by calling the `#wait`
11
+ # method. Each of the other threads calls `#count_down` when done with its work.
12
+ # When the latch counter reaches zero the waiting thread is unblocked and continues
13
+ # with its work. A `CountDownLatch` can be used only once. Its value cannot be reset.
14
+ class MutexCountDownLatch
15
+
16
+ # @!macro [attach] count_down_latch_method_initialize
17
+ #
18
+ # Create a new `CountDownLatch` with the initial `count`.
19
+ #
20
+ # @param [Fixnum] count the initial count
21
+ #
22
+ # @raise [ArgumentError] if `count` is not an integer or is less than zero
23
+ def initialize(count)
24
+ unless count.is_a?(Fixnum) && count >= 0
25
+ raise ArgumentError.new('count must be in integer greater than or equal zero')
26
+ end
27
+ @mutex = Mutex.new
28
+ @condition = Condition.new
29
+ @count = count
30
+ end
31
+
32
+ # @!macro [attach] count_down_latch_method_wait
33
+ #
34
+ # Block on the latch until the counter reaches zero or until `timeout` is reached.
35
+ #
36
+ # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil`
37
+ # to block indefinitely
38
+ # @return [Boolean] `true` if the `count` reaches zero else false on `timeout`
39
+ def wait(timeout = nil)
40
+ @mutex.synchronize do
41
+
42
+ remaining = Condition::Result.new(timeout)
43
+ while @count > 0 && remaining.can_wait?
44
+ remaining = @condition.wait(@mutex, remaining.remaining_time)
45
+ end
46
+
47
+ @count == 0
48
+ end
49
+ end
50
+
51
+ # @!macro [attach] count_down_latch_method_count_down
52
+ #
53
+ # Signal the latch to decrement the counter. Will signal all blocked threads when
54
+ # the `count` reaches zero.
55
+ def count_down
56
+ @mutex.synchronize do
57
+ @count -= 1 if @count > 0
58
+ @condition.broadcast if @count == 0
59
+ end
60
+ end
61
+
62
+ # @!macro [attach] count_down_latch_method_count
63
+ #
64
+ # The current value of the counter.
65
+ #
66
+ # @return [Fixnum] the current value of the counter
67
+ def count
68
+ @mutex.synchronize { @count }
69
+ end
70
+ end
71
+
72
+ if RUBY_PLATFORM == 'java'
73
+
74
+ # @!macro count_down_latch
75
+ class JavaCountDownLatch
76
+
77
+ # @!macro count_down_latch_method_initialize
78
+ def initialize(count)
79
+ unless count.is_a?(Fixnum) && count >= 0
80
+ raise ArgumentError.new('count must be in integer greater than or equal zero')
81
+ end
82
+ @latch = java.util.concurrent.CountDownLatch.new(count)
83
+ end
84
+
85
+ # @!macro count_down_latch_method_wait
86
+ def wait(timeout = nil)
87
+ if timeout.nil?
88
+ @latch.await
89
+ true
90
+ else
91
+ @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
92
+ end
93
+ end
94
+
95
+ # @!macro count_down_latch_method_count_down
96
+ def count_down
97
+ @latch.countDown
98
+ end
99
+
100
+ # @!macro count_down_latch_method_count
101
+ def count
102
+ @latch.getCount
103
+ end
104
+ end
105
+
106
+ # @!macro count_down_latch
107
+ class CountDownLatch < JavaCountDownLatch
108
+ end
109
+
110
+ else
111
+
112
+ # @!macro count_down_latch
113
+ class CountDownLatch < MutexCountDownLatch
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,106 @@
1
+ module Concurrent
2
+
3
+ class CyclicBarrier
4
+
5
+ Generation = Struct.new(:status)
6
+ private_constant :Generation
7
+
8
+ # Create a new `CyclicBarrier` that waits for `parties` threads
9
+ #
10
+ # @param [Fixnum] parties the number of parties
11
+ # @yield an optional block that will be executed that will be executed after the last thread arrives and before the others are released
12
+ #
13
+ # @raise [ArgumentError] if `parties` is not an integer or is less than zero
14
+ def initialize(parties, &block)
15
+ raise ArgumentError.new('count must be in integer greater than or equal zero') if !parties.is_a?(Fixnum) || parties < 1
16
+ @parties = parties
17
+ @mutex = Mutex.new
18
+ @condition = Condition.new
19
+ @number_waiting = 0
20
+ @action = block
21
+ @generation = Generation.new(:waiting)
22
+ end
23
+
24
+ # @return [Fixnum] the number of threads needed to pass the barrier
25
+ def parties
26
+ @parties
27
+ end
28
+
29
+ # @return [Fixnum] the number of threads currently waiting on the barrier
30
+ def number_waiting
31
+ @number_waiting
32
+ end
33
+
34
+ # Blocks on the barrier until the number of waiting threads is equal to `parties` or until `timeout` is reached or `reset` is called
35
+ # If a block has been passed to the constructor, it will be executed once by the last arrived thread before releasing the others
36
+ # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` to block indefinitely
37
+ # @return [Boolean] `true` if the `count` reaches zero else false on `timeout` or on `reset` or if the barrier is broken
38
+ def wait(timeout = nil)
39
+ @mutex.synchronize do
40
+
41
+ return false unless @generation.status == :waiting
42
+
43
+ @number_waiting += 1
44
+
45
+ if @number_waiting == @parties
46
+ @action.call if @action
47
+ set_status_and_restore(:fulfilled)
48
+ true
49
+ else
50
+ wait_for_wake_up(@generation, timeout)
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+
57
+ # resets the barrier to its initial state
58
+ # If there is at least one waiting thread, it will be woken up, the `wait` method will return false and the barrier will be broken
59
+ # If the barrier is broken, this method restores it to the original state
60
+ #
61
+ # @return [nil]
62
+ def reset
63
+ @mutex.synchronize do
64
+ set_status_and_restore(:reset)
65
+ end
66
+ end
67
+
68
+ # A barrier can be broken when:
69
+ # - a thread called the `reset` method while at least one other thread was waiting
70
+ # - at least one thread timed out on `wait` method
71
+ #
72
+ # A broken barrier can be restored using `reset` it's safer to create a new one
73
+ # @return [Boolean] true if the barrier is broken otherwise false
74
+ def broken?
75
+ @mutex.synchronize { @generation.status != :waiting }
76
+ end
77
+
78
+ private
79
+
80
+ def set_status_and_restore(new_status)
81
+ @generation.status = new_status
82
+ @condition.broadcast
83
+ @generation = Generation.new(:waiting)
84
+ @number_waiting = 0
85
+ end
86
+
87
+ def wait_for_wake_up(generation, timeout)
88
+ if wait_while_waiting(generation, timeout)
89
+ generation.status == :fulfilled
90
+ else
91
+ generation.status = :broken
92
+ @condition.broadcast
93
+ false
94
+ end
95
+ end
96
+
97
+ def wait_while_waiting(generation, timeout)
98
+ remaining = Condition::Result.new(timeout)
99
+ while generation.status == :waiting && remaining.can_wait?
100
+ remaining = @condition.wait(@mutex, remaining.remaining_time)
101
+ end
102
+ remaining.woken_up?
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,98 @@
1
+ require 'thread'
2
+ require 'concurrent/atomic/condition'
3
+
4
+ module Concurrent
5
+
6
+ # Old school kernel-style event reminiscent of Win32 programming in C++.
7
+ #
8
+ # When an `Event` is created it is in the `unset` state. Threads can choose to
9
+ # `#wait` on the event, blocking until released by another thread. When one
10
+ # thread wants to alert all blocking threads it calls the `#set` method which
11
+ # will then wake up all listeners. Once an `Event` has been set it remains set.
12
+ # New threads calling `#wait` will return immediately. An `Event` may be
13
+ # `#reset` at any time once it has been set.
14
+ #
15
+ # @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx
16
+ class Event
17
+
18
+ # Creates a new `Event` in the unset state. Threads calling `#wait` on the
19
+ # `Event` will block.
20
+ def initialize
21
+ @set = false
22
+ @mutex = Mutex.new
23
+ @condition = Condition.new
24
+ end
25
+
26
+ # Is the object in the set state?
27
+ #
28
+ # @return [Boolean] indicating whether or not the `Event` has been set
29
+ def set?
30
+ @mutex.lock
31
+ @set
32
+ ensure
33
+ @mutex.unlock
34
+ end
35
+
36
+ # Trigger the event, setting the state to `set` and releasing all threads
37
+ # waiting on the event. Has no effect if the `Event` has already been set.
38
+ #
39
+ # @return [Boolean] should always return `true`
40
+ def set
41
+ @mutex.lock
42
+ unless @set
43
+ @set = true
44
+ @condition.broadcast
45
+ end
46
+ true
47
+ ensure
48
+ @mutex.unlock
49
+ end
50
+
51
+ def try?
52
+ @mutex.lock
53
+
54
+ if @set
55
+ false
56
+ else
57
+ @set = true
58
+ @condition.broadcast
59
+ true
60
+ end
61
+
62
+ ensure
63
+ @mutex.unlock
64
+ end
65
+
66
+ # Reset a previously set event back to the `unset` state.
67
+ # Has no effect if the `Event` has not yet been set.
68
+ #
69
+ # @return [Boolean] should always return `true`
70
+ def reset
71
+ @mutex.lock
72
+ @set = false
73
+ true
74
+ ensure
75
+ @mutex.unlock
76
+ end
77
+
78
+ # Wait a given number of seconds for the `Event` to be set by another
79
+ # thread. Will wait forever when no `timeout` value is given. Returns
80
+ # immediately if the `Event` has already been set.
81
+ #
82
+ # @return [Boolean] true if the `Event` was set before timeout else false
83
+ def wait(timeout = nil)
84
+ @mutex.lock
85
+
86
+ unless @set
87
+ remaining = Condition::Result.new(timeout)
88
+ while !@set && remaining.can_wait?
89
+ remaining = @condition.wait(@mutex, remaining.remaining_time)
90
+ end
91
+ end
92
+
93
+ @set
94
+ ensure
95
+ @mutex.unlock
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,117 @@
1
+ module Concurrent
2
+
3
+ module ThreadLocalSymbolAllocator
4
+
5
+ COUNTER = AtomicFixnum.new
6
+
7
+ protected
8
+
9
+ def allocate_symbol
10
+ # Warning: this symbol may never be deallocated
11
+ @symbol = :"thread_local_symbol_#{COUNTER.increment}"
12
+ end
13
+
14
+ end
15
+
16
+ module ThreadLocalOldStorage
17
+
18
+ include ThreadLocalSymbolAllocator
19
+
20
+ protected
21
+
22
+ def allocate_storage
23
+ allocate_symbol
24
+ end
25
+
26
+ def get
27
+ Thread.current[@symbol]
28
+ end
29
+
30
+ def set(value)
31
+ Thread.current[@symbol] = value
32
+ end
33
+
34
+ end
35
+
36
+ module ThreadLocalNewStorage
37
+
38
+ include ThreadLocalSymbolAllocator
39
+
40
+ protected
41
+
42
+ def allocate_storage
43
+ allocate_symbol
44
+ end
45
+
46
+ def get
47
+ Thread.current.thread_variable_get(@symbol)
48
+ end
49
+
50
+ def set(value)
51
+ Thread.current.thread_variable_set(@symbol, value)
52
+ end
53
+
54
+ end
55
+
56
+ module ThreadLocalJavaStorage
57
+
58
+ protected
59
+
60
+ def allocate_storage
61
+ @var = java.lang.ThreadLocal.new
62
+ end
63
+
64
+ def get
65
+ @var.get
66
+ end
67
+
68
+ def set(value)
69
+ @var.set(value)
70
+ end
71
+
72
+ end
73
+
74
+ class ThreadLocalVar
75
+
76
+ NIL_SENTINEL = Object.new
77
+
78
+ if RUBY_PLATFORM == 'java'
79
+ include ThreadLocalJavaStorage
80
+ elsif Thread.current.respond_to?(:thread_variable_set)
81
+ include ThreadLocalNewStorage
82
+ else
83
+ include ThreadLocalOldStorage
84
+ end
85
+
86
+ def initialize(default = nil)
87
+ @default = default
88
+ allocate_storage
89
+ end
90
+
91
+ def value
92
+ value = get
93
+
94
+ if value.nil?
95
+ @default
96
+ elsif value == NIL_SENTINEL
97
+ nil
98
+ else
99
+ value
100
+ end
101
+ end
102
+
103
+ def value=(value)
104
+ if value.nil?
105
+ stored_value = NIL_SENTINEL
106
+ else
107
+ stored_value = value
108
+ end
109
+
110
+ set stored_value
111
+
112
+ value
113
+ end
114
+
115
+ end
116
+
117
+ end