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

Sign up to get free protection for your applications and to get access to all the features.
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