concurrent-ruby 1.0.5 → 1.1.0.pre1

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 (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile +39 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +203 -105
  6. data/Rakefile +278 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +304 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +24 -20
  24. data/lib/concurrent/agent.rb +7 -7
  25. data/lib/concurrent/array.rb +59 -32
  26. data/lib/concurrent/async.rb +4 -4
  27. data/lib/concurrent/atom.rb +9 -9
  28. data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
  29. data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
  30. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  31. data/lib/concurrent/atomic/atomic_reference.rb +176 -33
  32. data/lib/concurrent/atomic/count_down_latch.rb +6 -6
  33. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  34. data/lib/concurrent/atomic/event.rb +1 -1
  35. data/lib/concurrent/atomic/java_count_down_latch.rb +6 -5
  36. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
  37. data/lib/concurrent/atomic/read_write_lock.rb +2 -1
  38. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  39. data/lib/concurrent/atomic/semaphore.rb +8 -8
  40. data/lib/concurrent/atomic/thread_local_var.rb +7 -7
  41. data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
  42. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
  43. data/lib/concurrent/atomics.rb +0 -43
  44. data/lib/concurrent/collection/lock_free_stack.rb +127 -0
  45. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
  46. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
  47. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
  48. data/lib/concurrent/concern/dereferenceable.rb +1 -1
  49. data/lib/concurrent/concern/logging.rb +6 -1
  50. data/lib/concurrent/concern/observable.rb +7 -7
  51. data/lib/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/concurrent/configuration.rb +1 -6
  53. data/lib/concurrent/constants.rb +1 -1
  54. data/lib/concurrent/dataflow.rb +2 -1
  55. data/lib/concurrent/delay.rb +9 -7
  56. data/lib/concurrent/exchanger.rb +13 -21
  57. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  58. data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
  59. data/lib/concurrent/executor/executor_service.rb +15 -15
  60. data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
  61. data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
  62. data/lib/concurrent/executor/single_thread_executor.rb +2 -2
  63. data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
  64. data/lib/concurrent/executor/timer_set.rb +1 -1
  65. data/lib/concurrent/future.rb +4 -1
  66. data/lib/concurrent/hash.rb +53 -30
  67. data/lib/concurrent/ivar.rb +5 -6
  68. data/lib/concurrent/map.rb +20 -25
  69. data/lib/concurrent/maybe.rb +1 -1
  70. data/lib/concurrent/mutable_struct.rb +15 -14
  71. data/lib/concurrent/mvar.rb +2 -2
  72. data/lib/concurrent/promise.rb +53 -21
  73. data/lib/concurrent/promises.rb +1938 -0
  74. data/lib/concurrent/re_include.rb +58 -0
  75. data/lib/concurrent/set.rb +66 -0
  76. data/lib/concurrent/settable_struct.rb +1 -0
  77. data/lib/concurrent/synchronization.rb +4 -5
  78. data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
  79. data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
  80. data/lib/concurrent/synchronization/lockable_object.rb +6 -6
  81. data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
  82. data/lib/concurrent/synchronization/object.rb +8 -4
  83. data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
  84. data/lib/concurrent/synchronization/volatile.rb +11 -9
  85. data/lib/concurrent/thread_safe/util/data_structures.rb +55 -0
  86. data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
  87. data/lib/concurrent/timer_task.rb +5 -2
  88. data/lib/concurrent/tuple.rb +1 -1
  89. data/lib/concurrent/tvar.rb +2 -2
  90. data/lib/concurrent/utility/at_exit.rb +1 -1
  91. data/lib/concurrent/utility/engine.rb +2 -2
  92. data/lib/concurrent/utility/monotonic_time.rb +3 -3
  93. data/lib/concurrent/utility/native_extension_loader.rb +31 -33
  94. data/lib/concurrent/utility/processor_counter.rb +0 -2
  95. data/lib/concurrent/version.rb +2 -2
  96. metadata +35 -21
  97. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  98. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  99. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  100. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  101. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  102. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  103. data/lib/concurrent/edge.rb +0 -26
  104. data/lib/concurrent/lazy_register.rb +0 -81
  105. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  106. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  107. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -1,16 +1,11 @@
1
- require 'concurrent/synchronization'
2
- require 'concurrent/atomic_reference/direct_update'
3
- require 'concurrent/atomic_reference/numeric_cas_wrapper'
4
-
5
1
  module Concurrent
6
2
 
7
- # @!macro atomic_reference
8
- #
9
3
  # @!visibility private
10
4
  # @!macro internal_implementation_note
11
5
  class MutexAtomicReference < Synchronization::LockableObject
12
- include Concurrent::AtomicDirectUpdate
13
- include Concurrent::AtomicNumericCompareAndSetWrapper
6
+ include AtomicDirectUpdate
7
+ include AtomicNumericCompareAndSetWrapper
8
+ alias_method :compare_and_swap, :compare_and_set
14
9
 
15
10
  # @!macro atomic_reference_method_initialize
16
11
  def initialize(value = nil)
@@ -23,6 +23,6 @@ module Concurrent
23
23
  _compare_and_set(old_value, new_value)
24
24
  end
25
25
  end
26
- alias_method :compare_and_swap, :compare_and_set
26
+
27
27
  end
28
28
  end
@@ -1,46 +1,3 @@
1
- # @!macro [new] atomic_reference
2
- #
3
- # An object reference that may be updated atomically. All read and write
4
- # operations have java volatile semantic.
5
- #
6
- # @!macro thread_safe_variable_comparison
7
- #
8
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
9
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
10
- #
11
- # @!method initialize
12
- # @!macro [new] atomic_reference_method_initialize
13
- # @param [Object] value The initial value.
14
- #
15
- # @!method get
16
- # @!macro [new] atomic_reference_method_get
17
- # Gets the current value.
18
- # @return [Object] the current value
19
- #
20
- # @!method set
21
- # @!macro [new] atomic_reference_method_set
22
- # Sets to the given value.
23
- # @param [Object] new_value the new value
24
- # @return [Object] the new value
25
- #
26
- # @!method get_and_set
27
- # @!macro [new] atomic_reference_method_get_and_set
28
- # Atomically sets to the given value and returns the old value.
29
- # @param [Object] new_value the new value
30
- # @return [Object] the old value
31
- #
32
- # @!method compare_and_set
33
- # @!macro [new] atomic_reference_method_compare_and_set
34
- #
35
- # Atomically sets the value to the given updated value if
36
- # the current value == the expected value.
37
- #
38
- # @param [Object] old_value the expected value
39
- # @param [Object] new_value the new value
40
- #
41
- # @return [Boolean] `true` if successful. A `false` return indicates
42
- # that the actual value was not equal to the expected value.
43
-
44
1
  require 'concurrent/atomic/atomic_reference'
45
2
  require 'concurrent/atomic/atomic_boolean'
46
3
  require 'concurrent/atomic/atomic_fixnum'
@@ -0,0 +1,127 @@
1
+ module Concurrent
2
+
3
+ # @!visibility private
4
+ class LockFreeStack < Synchronization::Object
5
+
6
+ safe_initialization!
7
+
8
+ class Node
9
+ # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?
10
+
11
+ attr_reader :value, :next_node
12
+ # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
13
+ attr_writer :value
14
+
15
+ def initialize(value, next_node)
16
+ @value = value
17
+ @next_node = next_node
18
+ end
19
+
20
+ singleton_class.send :alias_method, :[], :new
21
+ end
22
+
23
+ class Empty < Node
24
+ def next_node
25
+ self
26
+ end
27
+ end
28
+
29
+ EMPTY = Empty[nil, nil]
30
+
31
+ attr_atomic(:head)
32
+ private :head, :head=, :swap_head, :compare_and_set_head, :update_head
33
+
34
+ def self.of1(value)
35
+ new Node[value, EMPTY]
36
+ end
37
+
38
+ def self.of2(value1, value2)
39
+ new Node[value1, Node[value2, EMPTY]]
40
+ end
41
+
42
+ def initialize(head = EMPTY)
43
+ super()
44
+ self.head = head
45
+ end
46
+
47
+ def empty?(head = self.head)
48
+ head.equal? EMPTY
49
+ end
50
+
51
+ def compare_and_push(head, value)
52
+ compare_and_set_head head, Node[value, head]
53
+ end
54
+
55
+ def push(value)
56
+ while true
57
+ current_head = head
58
+ return self if compare_and_set_head current_head, Node[value, current_head]
59
+ end
60
+ end
61
+
62
+ def peek
63
+ head
64
+ end
65
+
66
+ def compare_and_pop(head)
67
+ compare_and_set_head head, head.next_node
68
+ end
69
+
70
+ def pop
71
+ while true
72
+ current_head = head
73
+ return current_head.value if compare_and_set_head current_head, current_head.next_node
74
+ end
75
+ end
76
+
77
+ def compare_and_clear(head)
78
+ compare_and_set_head head, EMPTY
79
+ end
80
+
81
+ include Enumerable
82
+
83
+ def each(head = nil)
84
+ return to_enum(:each, head) unless block_given?
85
+ it = head || peek
86
+ until it.equal?(EMPTY)
87
+ yield it.value
88
+ it = it.next_node
89
+ end
90
+ self
91
+ end
92
+
93
+ def clear
94
+ while true
95
+ current_head = head
96
+ return false if current_head == EMPTY
97
+ return true if compare_and_set_head current_head, EMPTY
98
+ end
99
+ end
100
+
101
+ def clear_if(head)
102
+ compare_and_set_head head, EMPTY
103
+ end
104
+
105
+ def replace_if(head, new_head)
106
+ compare_and_set_head head, new_head
107
+ end
108
+
109
+ def clear_each(&block)
110
+ while true
111
+ current_head = head
112
+ return self if current_head == EMPTY
113
+ if compare_and_set_head current_head, EMPTY
114
+ each current_head, &block
115
+ return self
116
+ end
117
+ end
118
+ end
119
+
120
+ # @return [String] Short string representation.
121
+ def to_s
122
+ format '%s %s>', super[0..-2], to_a.to_s
123
+ end
124
+
125
+ alias_method :inspect, :to_s
126
+ end
127
+ end
@@ -289,7 +289,7 @@ module Concurrent
289
289
  end
290
290
  end
291
291
  elsif cas_hash(my_hash, my_hash | WAITING)
292
- force_aquire_lock(table, i)
292
+ force_acquire_lock(table, i)
293
293
  break
294
294
  end
295
295
  end
@@ -330,7 +330,7 @@ module Concurrent
330
330
  end
331
331
 
332
332
  private
333
- def force_aquire_lock(table, i)
333
+ def force_acquire_lock(table, i)
334
334
  cheap_synchronize do
335
335
  if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING
336
336
  cheap_wait
@@ -831,7 +831,7 @@ module Concurrent
831
831
  # no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table
832
832
  redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder))
833
833
  elsif Node.locked_hash?(node_hash = node.hash)
834
- locked_indexes ||= Array.new
834
+ locked_indexes ||= ::Array.new
835
835
  if bin < 0 && locked_arr_idx > 0
836
836
  locked_arr_idx -= 1
837
837
  i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin
@@ -10,7 +10,7 @@ module Concurrent
10
10
 
11
11
  # WARNING: all public methods of the class must operate on the @backend
12
12
  # directly without calling each other. This is important because of the
13
- # SynchronizedMapBackend which uses a non-reentrant mutex for perfomance
13
+ # SynchronizedMapBackend which uses a non-reentrant mutex for performance
14
14
  # reasons.
15
15
  def initialize(options = nil)
16
16
  @backend = {}
@@ -95,7 +95,6 @@ module Concurrent
95
95
  end
96
96
 
97
97
  def each_pair
98
- return enum_for :each_pair unless block_given?
99
98
  dupped_backend.each_pair do |k, v|
100
99
  yield k, v
101
100
  end
@@ -15,7 +15,7 @@ module Concurrent
15
15
  end
16
16
  private_constant :NonConcurrentPriorityQueueImplementation
17
17
 
18
- # @!macro [attach] priority_queue
18
+ # @!macro priority_queue
19
19
  #
20
20
  # A queue collection in which the elements are sorted based on their
21
21
  # comparison (spaceship) operator `<=>`. Items are added to the queue
@@ -45,7 +45,7 @@ module Concurrent
45
45
  # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
46
46
  #
47
47
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
48
- #
48
+ #
49
49
  # @!visibility private
50
50
  class NonConcurrentPriorityQueue < NonConcurrentPriorityQueueImplementation
51
51
 
@@ -60,83 +60,83 @@ module Concurrent
60
60
  alias_method :enq, :push
61
61
 
62
62
  # @!method initialize(opts = {})
63
- # @!macro [new] priority_queue_method_initialize
63
+ # @!macro priority_queue_method_initialize
64
64
  #
65
65
  # Create a new priority queue with no items.
66
- #
66
+ #
67
67
  # @param [Hash] opts the options for creating the queue
68
68
  # @option opts [Symbol] :order (:max) dictates the order in which items are
69
69
  # stored: from highest to lowest when `:max` or `:high`; from lowest to
70
70
  # highest when `:min` or `:low`
71
71
 
72
72
  # @!method clear
73
- # @!macro [new] priority_queue_method_clear
73
+ # @!macro priority_queue_method_clear
74
74
  #
75
75
  # Removes all of the elements from this priority queue.
76
76
 
77
77
  # @!method delete(item)
78
- # @!macro [new] priority_queue_method_delete
78
+ # @!macro priority_queue_method_delete
79
79
  #
80
80
  # Deletes all items from `self` that are equal to `item`.
81
- #
81
+ #
82
82
  # @param [Object] item the item to be removed from the queue
83
83
  # @return [Object] true if the item is found else false
84
84
 
85
85
  # @!method empty?
86
- # @!macro [new] priority_queue_method_empty
87
- #
86
+ # @!macro priority_queue_method_empty
87
+ #
88
88
  # Returns `true` if `self` contains no elements.
89
- #
89
+ #
90
90
  # @return [Boolean] true if there are no items in the queue else false
91
91
 
92
92
  # @!method include?(item)
93
- # @!macro [new] priority_queue_method_include
93
+ # @!macro priority_queue_method_include
94
94
  #
95
95
  # Returns `true` if the given item is present in `self` (that is, if any
96
96
  # element == `item`), otherwise returns false.
97
- #
97
+ #
98
98
  # @param [Object] item the item to search for
99
- #
99
+ #
100
100
  # @return [Boolean] true if the item is found else false
101
101
 
102
102
  # @!method length
103
- # @!macro [new] priority_queue_method_length
104
- #
103
+ # @!macro priority_queue_method_length
104
+ #
105
105
  # The current length of the queue.
106
- #
106
+ #
107
107
  # @return [Fixnum] the number of items in the queue
108
108
 
109
109
  # @!method peek
110
- # @!macro [new] priority_queue_method_peek
111
- #
110
+ # @!macro priority_queue_method_peek
111
+ #
112
112
  # Retrieves, but does not remove, the head of this queue, or returns `nil`
113
113
  # if this queue is empty.
114
- #
114
+ #
115
115
  # @return [Object] the head of the queue or `nil` when empty
116
116
 
117
117
  # @!method pop
118
- # @!macro [new] priority_queue_method_pop
119
- #
118
+ # @!macro priority_queue_method_pop
119
+ #
120
120
  # Retrieves and removes the head of this queue, or returns `nil` if this
121
121
  # queue is empty.
122
- #
122
+ #
123
123
  # @return [Object] the head of the queue or `nil` when empty
124
124
 
125
125
  # @!method push(item)
126
- # @!macro [new] priority_queue_method_push
127
- #
126
+ # @!macro priority_queue_method_push
127
+ #
128
128
  # Inserts the specified element into this priority queue.
129
- #
129
+ #
130
130
  # @param [Object] item the item to insert onto the queue
131
131
 
132
132
  # @!method self.from_list(list, opts = {})
133
- # @!macro [new] priority_queue_method_from_list
134
- #
133
+ # @!macro priority_queue_method_from_list
134
+ #
135
135
  # Create a new priority queue from the given list.
136
- #
136
+ #
137
137
  # @param [Enumerable] list the list to build the queue from
138
138
  # @param [Hash] opts the options for creating the queue
139
- #
139
+ #
140
140
  # @return [NonConcurrentPriorityQueue] the newly created and populated queue
141
141
  end
142
142
  end
@@ -32,7 +32,7 @@ module Concurrent
32
32
  synchronize{ @value = value }
33
33
  end
34
34
 
35
- # @!macro [attach] dereferenceable_set_deref_options
35
+ # @!macro dereferenceable_set_deref_options
36
36
  # Set the options which define the operations #value performs before
37
37
  # returning data to the caller (dereferencing).
38
38
  #
@@ -17,7 +17,12 @@ module Concurrent
17
17
  def log(level, progname, message = nil, &block)
18
18
  #NOTE: Cannot require 'concurrent/configuration' above due to circular references.
19
19
  # Assume that the gem has been initialized if we've gotten this far.
20
- (@logger || Concurrent.global_logger).call level, progname, message, &block
20
+ logger = if defined?(@logger) && @logger
21
+ @logger
22
+ else
23
+ Concurrent.global_logger
24
+ end
25
+ logger.call level, progname, message, &block
21
26
  rescue => error
22
27
  $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" +
23
28
  "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
@@ -49,7 +49,7 @@ module Concurrent
49
49
  # or an AtomicFixum)
50
50
  module Observable
51
51
 
52
- # @!macro [attach] observable_add_observer
52
+ # @!macro observable_add_observer
53
53
  #
54
54
  # Adds an observer to this set. If a block is passed, the observer will be
55
55
  # created by this method and no other params should be passed.
@@ -72,8 +72,8 @@ module Concurrent
72
72
  self
73
73
  end
74
74
 
75
- # @!macro [attach] observable_delete_observer
76
- #
75
+ # @!macro observable_delete_observer
76
+ #
77
77
  # Remove `observer` as an observer on this object so that it will no
78
78
  # longer receive notifications.
79
79
  #
@@ -83,17 +83,17 @@ module Concurrent
83
83
  observers.delete_observer(observer)
84
84
  end
85
85
 
86
- # @!macro [attach] observable_delete_observers
87
- #
86
+ # @!macro observable_delete_observers
87
+ #
88
88
  # Remove all observers associated with this object.
89
- #
89
+ #
90
90
  # @return [Observable] self
91
91
  def delete_observers
92
92
  observers.delete_observers
93
93
  self
94
94
  end
95
95
 
96
- # @!macro [attach] observable_count_observers
96
+ # @!macro observable_count_observers
97
97
  #
98
98
  # Return the number of observers associated with this object.
99
99
  #
@@ -175,13 +175,8 @@ module Concurrent
175
175
  end
176
176
 
177
177
  def self.new_io_executor(opts = {})
178
- ThreadPoolExecutor.new(
179
- min_threads: [2, Concurrent.processor_count].max,
180
- max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
181
- # max_threads: 1000,
178
+ CachedThreadPool.new(
182
179
  auto_terminate: opts.fetch(:auto_terminate, true),
183
- idletime: 60, # 1 minute
184
- max_queue: 0, # unlimited
185
180
  fallback_policy: :abort # shouldn't matter -- 0 max queue
186
181
  )
187
182
  end