concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -1,12 +1,82 @@
1
- require 'concurrent/atomic'
1
+ # @!macro [new] atomic_reference
2
+ #
3
+ # An object reference that may be updated atomically.
4
+ #
5
+ # Testing with ruby 2.1.2
6
+ #
7
+ # *** Sequential updates ***
8
+ # user system total real
9
+ # no lock 0.000000 0.000000 0.000000 ( 0.005502)
10
+ # mutex 0.030000 0.000000 0.030000 ( 0.025158)
11
+ # MutexAtomicReference 0.100000 0.000000 0.100000 ( 0.103096)
12
+ # CAtomicReference 0.040000 0.000000 0.040000 ( 0.034012)
13
+ #
14
+ # *** Parallel updates ***
15
+ # user system total real
16
+ # no lock 0.010000 0.000000 0.010000 ( 0.009387)
17
+ # mutex 0.030000 0.010000 0.040000 ( 0.032545)
18
+ # MutexAtomicReference 0.830000 2.280000 3.110000 ( 2.146622)
19
+ # CAtomicReference 0.040000 0.000000 0.040000 ( 0.038332)
20
+ #
21
+ # Testing with jruby 1.9.3
22
+ #
23
+ # *** Sequential updates ***
24
+ # user system total real
25
+ # no lock 0.170000 0.000000 0.170000 ( 0.051000)
26
+ # mutex 0.370000 0.010000 0.380000 ( 0.121000)
27
+ # MutexAtomicReference 1.530000 0.020000 1.550000 ( 0.471000)
28
+ # JavaAtomicReference 0.370000 0.010000 0.380000 ( 0.112000)
29
+ #
30
+ # *** Parallel updates ***
31
+ # user system total real
32
+ # no lock 0.390000 0.000000 0.390000 ( 0.105000)
33
+ # mutex 0.480000 0.040000 0.520000 ( 0.145000)
34
+ # MutexAtomicReference 1.600000 0.180000 1.780000 ( 0.511000)
35
+ # JavaAtomicReference 0.460000 0.010000 0.470000 ( 0.131000)
36
+ #
37
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
38
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
39
+ #
40
+ # @!method initialize
41
+ # @!macro [new] atomic_reference_method_initialize
42
+ # @param [Object] value The initial value.
43
+ #
44
+ # @!method get
45
+ # @!macro [new] atomic_reference_method_get
46
+ # Gets the current value.
47
+ # @return [Object] the current value
48
+ #
49
+ # @!method set
50
+ # @!macro [new] atomic_reference_method_set
51
+ # Sets to the given value.
52
+ # @param [Object] new_value the new value
53
+ # @return [Object] the new value
54
+ #
55
+ # @!method get_and_set
56
+ # @!macro [new] atomic_reference_method_get_and_set
57
+ # Atomically sets to the given value and returns the old value.
58
+ # @param [Object] new_value the new value
59
+ # @return [Object] the old value
60
+ #
61
+ # @!method compare_and_set
62
+ # @!macro [new] atomic_reference_method_compare_and_set
63
+ #
64
+ # Atomically sets the value to the given updated value if
65
+ # the current value == the expected value.
66
+ #
67
+ # @param [Object] old_value the expected value
68
+ # @param [Object] new_value the new value
69
+ #
70
+ # @return [Boolean] `true` if successful. A `false` return indicates
71
+ # that the actual value was not equal to the expected value.
72
+
73
+ require 'concurrent/atomic/atomic_reference'
2
74
  require 'concurrent/atomic/atomic_boolean'
3
75
  require 'concurrent/atomic/atomic_fixnum'
4
76
  require 'concurrent/atomic/condition'
5
- require 'concurrent/atomic/copy_on_notify_observer_set'
6
- require 'concurrent/atomic/copy_on_write_observer_set'
7
77
  require 'concurrent/atomic/cyclic_barrier'
8
78
  require 'concurrent/atomic/count_down_latch'
9
79
  require 'concurrent/atomic/event'
10
- require 'concurrent/atomic/synchronization'
80
+ require 'concurrent/atomic/read_write_lock'
11
81
  require 'concurrent/atomic/semaphore'
12
82
  require 'concurrent/atomic/thread_local_var'
@@ -0,0 +1,115 @@
1
+ require 'concurrent/synchronization'
2
+
3
+ module Concurrent
4
+ module Collection
5
+
6
+ # A thread safe observer set implemented using copy-on-read approach:
7
+ # observers are added and removed from a thread safe collection; every time
8
+ # a notification is required the internal data structure is copied to
9
+ # prevent concurrency issues
10
+ #
11
+ # @api private
12
+ class CopyOnNotifyObserverSet < Synchronization::Object
13
+
14
+ def initialize
15
+ super()
16
+ synchronize { ns_initialize }
17
+ end
18
+
19
+ # Adds an observer to this set. If a block is passed, the observer will be
20
+ # created by this method and no other params should be passed
21
+ #
22
+ # @param [Object] observer the observer to add
23
+ # @param [Symbol] func the function to call on the observer during notification.
24
+ # Default is :update
25
+ # @return [Object] the added observer
26
+ def add_observer(observer=nil, func=:update, &block)
27
+ if observer.nil? && block.nil?
28
+ raise ArgumentError, 'should pass observer as a first argument or block'
29
+ elsif observer && block
30
+ raise ArgumentError.new('cannot provide both an observer and a block')
31
+ end
32
+
33
+ if block
34
+ observer = block
35
+ func = :call
36
+ end
37
+
38
+ synchronize do
39
+ @observers[observer] = func
40
+ observer
41
+ end
42
+ end
43
+
44
+ # @param [Object] observer the observer to remove
45
+ # @return [Object] the deleted observer
46
+ def delete_observer(observer)
47
+ synchronize do
48
+ @observers.delete(observer)
49
+ observer
50
+ end
51
+ end
52
+
53
+ # Deletes all observers
54
+ # @return [CopyOnWriteObserverSet] self
55
+ def delete_observers
56
+ synchronize do
57
+ @observers.clear
58
+ self
59
+ end
60
+ end
61
+
62
+ # @return [Integer] the observers count
63
+ def count_observers
64
+ synchronize { @observers.count }
65
+ end
66
+
67
+ # Notifies all registered observers with optional args
68
+ # @param [Object] args arguments to be passed to each observer
69
+ # @return [CopyOnWriteObserverSet] self
70
+ def notify_observers(*args, &block)
71
+ observers = duplicate_observers
72
+ notify_to(observers, *args, &block)
73
+ self
74
+ end
75
+
76
+ # Notifies all registered observers with optional args and deletes them.
77
+ #
78
+ # @param [Object] args arguments to be passed to each observer
79
+ # @return [CopyOnWriteObserverSet] self
80
+ def notify_and_delete_observers(*args, &block)
81
+ observers = duplicate_and_clear_observers
82
+ notify_to(observers, *args, &block)
83
+ self
84
+ end
85
+
86
+ protected
87
+
88
+ def ns_initialize
89
+ @observers = {}
90
+ end
91
+
92
+ private
93
+
94
+ def duplicate_and_clear_observers
95
+ synchronize do
96
+ observers = @observers.dup
97
+ @observers.clear
98
+ observers
99
+ end
100
+ end
101
+
102
+ def duplicate_observers
103
+ synchronize { observers = @observers.dup }
104
+ end
105
+
106
+ def notify_to(observers, *args)
107
+ raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
108
+ observers.each do |observer, function|
109
+ args = yield if block_given?
110
+ observer.send(function, *args)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,119 @@
1
+ require 'concurrent/synchronization'
2
+
3
+ module Concurrent
4
+ module Collection
5
+
6
+ # A thread safe observer set implemented using copy-on-write approach:
7
+ # every time an observer is added or removed the whole internal data structure is
8
+ # duplicated and replaced with a new one.
9
+ #
10
+ # @api private
11
+ class CopyOnWriteObserverSet < Synchronization::Object
12
+
13
+ def initialize
14
+ super()
15
+ synchronize { ns_initialize }
16
+ end
17
+
18
+ # Adds an observer to this set
19
+ # If a block is passed, the observer will be created by this method and no
20
+ # other params should be passed
21
+ # @param [Object] observer the observer to add
22
+ # @param [Symbol] func the function to call on the observer during notification.
23
+ # Default is :update
24
+ # @return [Object] the added observer
25
+ def add_observer(observer=nil, func=:update, &block)
26
+ if observer.nil? && block.nil?
27
+ raise ArgumentError, 'should pass observer as a first argument or block'
28
+ elsif observer && block
29
+ raise ArgumentError.new('cannot provide both an observer and a block')
30
+ end
31
+
32
+ if block
33
+ observer = block
34
+ func = :call
35
+ end
36
+
37
+ synchronize do
38
+ new_observers = @observers.dup
39
+ new_observers[observer] = func
40
+ @observers = new_observers
41
+ observer
42
+ end
43
+ end
44
+
45
+ # @param [Object] observer the observer to remove
46
+ # @return [Object] the deleted observer
47
+ def delete_observer(observer)
48
+ synchronize do
49
+ new_observers = @observers.dup
50
+ new_observers.delete(observer)
51
+ @observers = new_observers
52
+ observer
53
+ end
54
+ end
55
+
56
+ # Deletes all observers
57
+ # @return [CopyOnWriteObserverSet] self
58
+ def delete_observers
59
+ self.observers = {}
60
+ self
61
+ end
62
+
63
+ # @return [Integer] the observers count
64
+ def count_observers
65
+ observers.count
66
+ end
67
+
68
+ # Notifies all registered observers with optional args
69
+ # @param [Object] args arguments to be passed to each observer
70
+ # @return [CopyOnWriteObserverSet] self
71
+ def notify_observers(*args, &block)
72
+ notify_to(observers, *args, &block)
73
+ self
74
+ end
75
+
76
+ # Notifies all registered observers with optional args and deletes them.
77
+ #
78
+ # @param [Object] args arguments to be passed to each observer
79
+ # @return [CopyOnWriteObserverSet] self
80
+ def notify_and_delete_observers(*args, &block)
81
+ old = clear_observers_and_return_old
82
+ notify_to(old, *args, &block)
83
+ self
84
+ end
85
+
86
+ protected
87
+
88
+ def ns_initialize
89
+ @observers = {}
90
+ end
91
+
92
+ private
93
+
94
+ def notify_to(observers, *args)
95
+ raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
96
+ observers.each do |observer, function|
97
+ args = yield if block_given?
98
+ observer.send(function, *args)
99
+ end
100
+ end
101
+
102
+ def observers
103
+ synchronize { @observers }
104
+ end
105
+
106
+ def observers=(new_set)
107
+ synchronize { @observers = new_set }
108
+ end
109
+
110
+ def clear_observers_and_return_old
111
+ synchronize do
112
+ old_observers = @observers
113
+ @observers = {}
114
+ old_observers
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -1,305 +1,360 @@
1
1
  module Concurrent
2
+ module Collection
2
3
 
3
- # @!macro [attach] priority_queue
4
- #
5
- # A queue collection in which the elements are sorted based on their
6
- # comparison (spaceship) operator `<=>`. Items are added to the queue
7
- # at a position relative to their priority. On removal the element
8
- # with the "highest" priority is removed. By default the sort order is
9
- # from highest to lowest, but a lowest-to-highest sort order can be
10
- # set on construction.
11
- #
12
- # The API is based on the `Queue` class from the Ruby standard library.
13
- #
14
- # The pure Ruby implementation, `MutexPriorityQueue` uses a heap algorithm
15
- # stored in an array. The algorithm is based on the work of Robert Sedgewick
16
- # and Kevin Wayne.
17
- #
18
- # The JRuby native implementation is a thin wrapper around the standard
19
- # library `java.util.PriorityQueue`.
20
- #
21
- # When running under JRuby the class `PriorityQueue` extends `JavaPriorityQueue`.
22
- # When running under all other interpreters it extends `MutexPriorityQueue`.
23
- #
24
- # @note This implementation is *not* thread safe and performs no blocking.
25
- #
26
- # @see http://en.wikipedia.org/wiki/Priority_queue
27
- # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html
28
- #
29
- # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6
30
- # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
31
- #
32
- # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
33
- class MutexPriorityQueue
34
-
35
- # @!macro [attach] priority_queue_method_initialize
4
+ # @!macro [attach] priority_queue
36
5
  #
37
- # Create a new priority queue with no items.
38
- #
39
- # @param [Hash] opts the options for creating the queue
40
- # @option opts [Symbol] :order (:max) dictates the order in which items are
41
- # stored: from highest to lowest when `:max` or `:high`; from lowest to
42
- # highest when `:min` or `:low`
43
- def initialize(opts = {})
44
- order = opts.fetch(:order, :max)
45
- @comparator = [:min, :low].include?(order) ? -1 : 1
46
- clear
47
- end
48
-
49
- # @!macro [attach] priority_queue_method_clear
6
+ # A queue collection in which the elements are sorted based on their
7
+ # comparison (spaceship) operator `<=>`. Items are added to the queue
8
+ # at a position relative to their priority. On removal the element
9
+ # with the "highest" priority is removed. By default the sort order is
10
+ # from highest to lowest, but a lowest-to-highest sort order can be
11
+ # set on construction.
50
12
  #
51
- # Removes all of the elements from this priority queue.
52
- def clear
53
- @queue = [nil]
54
- @length = 0
55
- true
56
- end
57
-
58
- # @!macro [attach] priority_queue_method_delete
13
+ # The API is based on the `Queue` class from the Ruby standard library.
59
14
  #
60
- # Deletes all items from `self` that are equal to `item`.
61
- #
62
- # @param [Object] item the item to be removed from the queue
63
- # @return [Object] true if the item is found else false
64
- def delete(item)
65
- original_length = @length
66
- k = 1
67
- while k <= @length
68
- if @queue[k] == item
69
- swap(k, @length)
70
- @length -= 1
71
- sink(k)
72
- @queue.pop
73
- else
74
- k += 1
75
- end
76
- end
77
- @length != original_length
78
- end
79
-
80
- # @!macro [attach] priority_queue_method_empty
81
- #
82
- # Returns `true` if `self` contains no elements.
83
- #
84
- # @return [Boolean] true if there are no items in the queue else false
85
- def empty?
86
- size == 0
87
- end
88
-
89
- # @!macro [attach] priority_queue_method_include
15
+ # The pure Ruby implementation, `MutexPriorityQueue` uses a heap algorithm
16
+ # stored in an array. The algorithm is based on the work of Robert Sedgewick
17
+ # and Kevin Wayne.
90
18
  #
91
- # Returns `true` if the given item is present in `self` (that is, if any
92
- # element == `item`), otherwise returns false.
93
- #
94
- # @param [Object] item the item to search for
95
- #
96
- # @return [Boolean] true if the item is found else false
97
- def include?(item)
98
- @queue.include?(item)
99
- end
100
- alias_method :has_priority?, :include?
101
-
102
- # @!macro [attach] priority_queue_method_length
103
- #
104
- # The current length of the queue.
105
- #
106
- # @return [Fixnum] the number of items in the queue
107
- def length
108
- @length
109
- end
110
- alias_method :size, :length
111
-
112
- # @!macro [attach] priority_queue_method_peek
113
- #
114
- # Retrieves, but does not remove, the head of this queue, or returns `nil`
115
- # if this queue is empty.
116
- #
117
- # @return [Object] the head of the queue or `nil` when empty
118
- def peek
119
- @queue[1]
120
- end
121
-
122
- # @!macro [attach] priority_queue_method_pop
123
- #
124
- # Retrieves and removes the head of this queue, or returns `nil` if this
125
- # queue is empty.
126
- #
127
- # @return [Object] the head of the queue or `nil` when empty
128
- def pop
129
- max = @queue[1]
130
- swap(1, @length)
131
- @length -= 1
132
- sink(1)
133
- @queue.pop
134
- max
135
- end
136
- alias_method :deq, :pop
137
- alias_method :shift, :pop
138
-
139
- # @!macro [attach] priority_queue_method_push
140
- #
141
- # Inserts the specified element into this priority queue.
142
- #
143
- # @param [Object] item the item to insert onto the queue
144
- def push(item)
145
- @length += 1
146
- @queue << item
147
- swim(@length)
148
- true
149
- end
150
- alias_method :<<, :push
151
- alias_method :enq, :push
152
-
153
- # @!macro [attach] priority_queue_method_from_list
154
- #
155
- # Create a new priority queue from the given list.
156
- #
157
- # @param [Enumerable] list the list to build the queue from
158
- # @param [Hash] opts the options for creating the queue
159
- #
160
- # @return [PriorityQueue] the newly created and populated queue
161
- def self.from_list(list, opts = {})
162
- queue = new(opts)
163
- list.each{|item| queue << item }
164
- queue
165
- end
166
-
167
- protected
168
-
169
- # Exchange the values at the given indexes within the internal array.
170
- #
171
- # @param [Integer] x the first index to swap
172
- # @param [Integer] y the second index to swap
173
- #
174
- # @!visibility private
175
- def swap(x, y)
176
- temp = @queue[x]
177
- @queue[x] = @queue[y]
178
- @queue[y] = temp
179
- end
180
-
181
- # Are the items at the given indexes ordered based on the priority
182
- # order specified at construction?
19
+ # The JRuby native implementation is a thin wrapper around the standard
20
+ # library `java.util.PriorityQueue`.
183
21
  #
184
- # @param [Integer] x the first index from which to retrieve a comparable value
185
- # @param [Integer] y the second index from which to retrieve a comparable value
22
+ # When running under JRuby the class `PriorityQueue` extends `JavaPriorityQueue`.
23
+ # When running under all other interpreters it extends `MutexPriorityQueue`.
186
24
  #
187
- # @return [Boolean] true if the two elements are in the correct priority order
188
- # else false
189
- #
190
- # @!visibility private
191
- def ordered?(x, y)
192
- (@queue[x] <=> @queue[y]) == @comparator
193
- end
194
-
195
- # Percolate down to maintain heap invariant.
196
- #
197
- # @param [Integer] k the index at which to start the percolation
198
- #
199
- # @!visibility private
200
- def sink(k)
201
- while (j = (2 * k)) <= @length do
202
- j += 1 if j < @length && ! ordered?(j, j+1)
203
- break if ordered?(k, j)
204
- swap(k, j)
205
- k = j
206
- end
207
- end
208
-
209
- # Percolate up to maintain heap invariant.
210
- #
211
- # @param [Integer] k the index at which to start the percolation
25
+ # @note This implementation is *not* thread safe.
26
+ #
27
+ # @see http://en.wikipedia.org/wiki/Priority_queue
28
+ # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html
29
+ #
30
+ # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6
31
+ # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
32
+ #
33
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
212
34
  #
213
35
  # @!visibility private
214
- def swim(k)
215
- while k > 1 && ! ordered?(k/2, k) do
216
- swap(k, k/2)
217
- k = k/2
218
- end
219
- end
220
- end
36
+ # @!macro internal_implementation_note
37
+ class MutexPriorityQueue
221
38
 
222
- if RUBY_PLATFORM == 'java'
223
-
224
- # @!macro priority_queue
225
- class JavaPriorityQueue
226
-
227
- # @!macro priority_queue_method_initialize
39
+ # @!macro [attach] priority_queue_method_initialize
40
+ #
41
+ # Create a new priority queue with no items.
42
+ #
43
+ # @param [Hash] opts the options for creating the queue
44
+ # @option opts [Symbol] :order (:max) dictates the order in which items are
45
+ # stored: from highest to lowest when `:max` or `:high`; from lowest to
46
+ # highest when `:min` or `:low`
228
47
  def initialize(opts = {})
229
48
  order = opts.fetch(:order, :max)
230
- if [:min, :low].include?(order)
231
- @queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity
232
- else
233
- @queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder())
234
- end
49
+ @comparator = [:min, :low].include?(order) ? -1 : 1
50
+ clear
235
51
  end
236
52
 
237
- # @!macro priority_queue_method_clear
53
+ # @!macro [attach] priority_queue_method_clear
54
+ #
55
+ # Removes all of the elements from this priority queue.
238
56
  def clear
239
- @queue.clear
57
+ @queue = [nil]
58
+ @length = 0
240
59
  true
241
60
  end
242
61
 
243
- # @!macro priority_queue_method_delete
62
+ # @!macro [attach] priority_queue_method_delete
63
+ #
64
+ # Deletes all items from `self` that are equal to `item`.
65
+ #
66
+ # @param [Object] item the item to be removed from the queue
67
+ # @return [Object] true if the item is found else false
244
68
  def delete(item)
245
- found = false
246
- while @queue.remove(item) do
247
- found = true
69
+ original_length = @length
70
+ k = 1
71
+ while k <= @length
72
+ if @queue[k] == item
73
+ swap(k, @length)
74
+ @length -= 1
75
+ sink(k)
76
+ @queue.pop
77
+ else
78
+ k += 1
79
+ end
248
80
  end
249
- found
81
+ @length != original_length
250
82
  end
251
83
 
252
- # @!macro priority_queue_method_empty
84
+ # @!macro [attach] priority_queue_method_empty
85
+ #
86
+ # Returns `true` if `self` contains no elements.
87
+ #
88
+ # @return [Boolean] true if there are no items in the queue else false
253
89
  def empty?
254
- @queue.size == 0
90
+ size == 0
255
91
  end
256
92
 
257
- # @!macro priority_queue_method_include
93
+ # @!macro [attach] priority_queue_method_include
94
+ #
95
+ # Returns `true` if the given item is present in `self` (that is, if any
96
+ # element == `item`), otherwise returns false.
97
+ #
98
+ # @param [Object] item the item to search for
99
+ #
100
+ # @return [Boolean] true if the item is found else false
258
101
  def include?(item)
259
- @queue.contains(item)
102
+ @queue.include?(item)
260
103
  end
261
104
  alias_method :has_priority?, :include?
262
105
 
263
- # @!macro priority_queue_method_length
106
+ # @!macro [attach] priority_queue_method_length
107
+ #
108
+ # The current length of the queue.
109
+ #
110
+ # @return [Fixnum] the number of items in the queue
264
111
  def length
265
- @queue.size
112
+ @length
266
113
  end
267
114
  alias_method :size, :length
268
115
 
269
- # @!macro priority_queue_method_peek
116
+ # @!macro [attach] priority_queue_method_peek
117
+ #
118
+ # Retrieves, but does not remove, the head of this queue, or returns `nil`
119
+ # if this queue is empty.
120
+ #
121
+ # @return [Object] the head of the queue or `nil` when empty
270
122
  def peek
271
- @queue.peek
123
+ @queue[1]
272
124
  end
273
125
 
274
- # @!macro priority_queue_method_pop
126
+ # @!macro [attach] priority_queue_method_pop
127
+ #
128
+ # Retrieves and removes the head of this queue, or returns `nil` if this
129
+ # queue is empty.
130
+ #
131
+ # @return [Object] the head of the queue or `nil` when empty
275
132
  def pop
276
- @queue.poll
133
+ max = @queue[1]
134
+ swap(1, @length)
135
+ @length -= 1
136
+ sink(1)
137
+ @queue.pop
138
+ max
277
139
  end
278
140
  alias_method :deq, :pop
279
141
  alias_method :shift, :pop
280
142
 
281
- # @!macro priority_queue_method_push
143
+ # @!macro [attach] priority_queue_method_push
144
+ #
145
+ # Inserts the specified element into this priority queue.
146
+ #
147
+ # @param [Object] item the item to insert onto the queue
282
148
  def push(item)
283
- @queue.add(item)
149
+ @length += 1
150
+ @queue << item
151
+ swim(@length)
152
+ true
284
153
  end
285
154
  alias_method :<<, :push
286
155
  alias_method :enq, :push
287
156
 
288
- # @!macro priority_queue_method_from_list
157
+ # @!macro [attach] priority_queue_method_from_list
158
+ #
159
+ # Create a new priority queue from the given list.
160
+ #
161
+ # @param [Enumerable] list the list to build the queue from
162
+ # @param [Hash] opts the options for creating the queue
163
+ #
164
+ # @return [PriorityQueue] the newly created and populated queue
289
165
  def self.from_list(list, opts = {})
290
166
  queue = new(opts)
291
167
  list.each{|item| queue << item }
292
168
  queue
293
169
  end
170
+
171
+ protected
172
+
173
+ # Exchange the values at the given indexes within the internal array.
174
+ #
175
+ # @param [Integer] x the first index to swap
176
+ # @param [Integer] y the second index to swap
177
+ #
178
+ # @!visibility private
179
+ def swap(x, y)
180
+ temp = @queue[x]
181
+ @queue[x] = @queue[y]
182
+ @queue[y] = temp
183
+ end
184
+
185
+ # Are the items at the given indexes ordered based on the priority
186
+ # order specified at construction?
187
+ #
188
+ # @param [Integer] x the first index from which to retrieve a comparable value
189
+ # @param [Integer] y the second index from which to retrieve a comparable value
190
+ #
191
+ # @return [Boolean] true if the two elements are in the correct priority order
192
+ # else false
193
+ #
194
+ # @!visibility private
195
+ def ordered?(x, y)
196
+ (@queue[x] <=> @queue[y]) == @comparator
197
+ end
198
+
199
+ # Percolate down to maintain heap invariant.
200
+ #
201
+ # @param [Integer] k the index at which to start the percolation
202
+ #
203
+ # @!visibility private
204
+ def sink(k)
205
+ while (j = (2 * k)) <= @length do
206
+ j += 1 if j < @length && ! ordered?(j, j+1)
207
+ break if ordered?(k, j)
208
+ swap(k, j)
209
+ k = j
210
+ end
211
+ end
212
+
213
+ # Percolate up to maintain heap invariant.
214
+ #
215
+ # @param [Integer] k the index at which to start the percolation
216
+ #
217
+ # @!visibility private
218
+ def swim(k)
219
+ while k > 1 && ! ordered?(k/2, k) do
220
+ swap(k, k/2)
221
+ k = k/2
222
+ end
223
+ end
294
224
  end
295
225
 
296
- # @!macro priority_queue
297
- class PriorityQueue < JavaPriorityQueue
226
+ if Concurrent.on_jruby?
227
+
228
+ # @!macro priority_queue
229
+ #
230
+ # @!visibility private
231
+ # @!macro internal_implementation_note
232
+ class JavaPriorityQueue
233
+
234
+ # @!macro priority_queue_method_initialize
235
+ def initialize(opts = {})
236
+ order = opts.fetch(:order, :max)
237
+ if [:min, :low].include?(order)
238
+ @queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity
239
+ else
240
+ @queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder())
241
+ end
242
+ end
243
+
244
+ # @!macro priority_queue_method_clear
245
+ def clear
246
+ @queue.clear
247
+ true
248
+ end
249
+
250
+ # @!macro priority_queue_method_delete
251
+ def delete(item)
252
+ found = false
253
+ while @queue.remove(item) do
254
+ found = true
255
+ end
256
+ found
257
+ end
258
+
259
+ # @!macro priority_queue_method_empty
260
+ def empty?
261
+ @queue.size == 0
262
+ end
263
+
264
+ # @!macro priority_queue_method_include
265
+ def include?(item)
266
+ @queue.contains(item)
267
+ end
268
+ alias_method :has_priority?, :include?
269
+
270
+ # @!macro priority_queue_method_length
271
+ def length
272
+ @queue.size
273
+ end
274
+ alias_method :size, :length
275
+
276
+ # @!macro priority_queue_method_peek
277
+ def peek
278
+ @queue.peek
279
+ end
280
+
281
+ # @!macro priority_queue_method_pop
282
+ def pop
283
+ @queue.poll
284
+ end
285
+ alias_method :deq, :pop
286
+ alias_method :shift, :pop
287
+
288
+ # @!macro priority_queue_method_push
289
+ def push(item)
290
+ @queue.add(item)
291
+ end
292
+ alias_method :<<, :push
293
+ alias_method :enq, :push
294
+
295
+ # @!macro priority_queue_method_from_list
296
+ def self.from_list(list, opts = {})
297
+ queue = new(opts)
298
+ list.each{|item| queue << item }
299
+ queue
300
+ end
301
+ end
298
302
  end
299
- else
303
+
304
+ # @!visibility private
305
+ # @!macro internal_implementation_note
306
+ PriorityQueueImplementation = case
307
+ when Concurrent.on_jruby?
308
+ JavaPriorityQueue
309
+ else
310
+ MutexPriorityQueue
311
+ end
312
+ private_constant :PriorityQueueImplementation
300
313
 
301
314
  # @!macro priority_queue
302
- class PriorityQueue < MutexPriorityQueue
315
+ #
316
+ # @!visibility private
317
+ class PriorityQueue < PriorityQueueImplementation
318
+
319
+ alias_method :has_priority?, :include?
320
+
321
+ alias_method :size, :length
322
+
323
+ alias_method :deq, :pop
324
+ alias_method :shift, :pop
325
+
326
+ alias_method :<<, :push
327
+ alias_method :enq, :push
328
+
329
+ # @!method initialize(opts = {})
330
+ # @!macro priority_queue_method_initialize
331
+
332
+ # @!method clear
333
+ # @!macro priority_queue_method_clear
334
+
335
+ # @!method delete(item)
336
+ # @!macro priority_queue_method_delete
337
+
338
+ # @!method empty?
339
+ # @!macro priority_queue_method_empty
340
+
341
+ # @!method include?(item)
342
+ # @!macro priority_queue_method_include
343
+
344
+ # @!method length
345
+ # @!macro priority_queue_method_length
346
+
347
+ # @!method peek
348
+ # @!macro priority_queue_method_peek
349
+
350
+ # @!method pop
351
+ # @!macro priority_queue_method_pop
352
+
353
+ # @!method push(item)
354
+ # @!macro priority_queue_method_push
355
+
356
+ # @!method self.from_list(list, opts = {})
357
+ # @!macro priority_queue_method_from_list
303
358
  end
304
359
  end
305
360
  end