concurrent-ruby 1.1.6 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/Gemfile +3 -8
- data/{LICENSE.md → LICENSE.txt} +18 -20
- data/README.md +43 -23
- data/Rakefile +32 -35
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
- data/lib/concurrent-ruby/concurrent/array.rb +1 -1
- data/lib/concurrent-ruby/concurrent/async.rb +10 -20
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +2 -2
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +18 -2
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +4 -6
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +52 -42
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +26 -5
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +16 -13
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +20 -3
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +17 -1
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +37 -38
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +5 -5
- data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +2 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +1 -1
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/map.rb +13 -4
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +29 -16
- data/lib/concurrent-ruby/concurrent/set.rb +14 -6
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +3 -5
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -34
- data/lib/concurrent-ruby/concurrent/tvar.rb +19 -56
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +67 -35
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +2 -35
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
- metadata +10 -10
@@ -23,7 +23,14 @@ module Concurrent
|
|
23
23
|
|
24
24
|
synchronize do
|
25
25
|
try_acquire_timed(permits, nil)
|
26
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
return unless block_given?
|
29
|
+
|
30
|
+
begin
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
release(permits)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -48,13 +55,22 @@ module Concurrent
|
|
48
55
|
Utility::NativeInteger.ensure_integer_and_bounds permits
|
49
56
|
Utility::NativeInteger.ensure_positive permits
|
50
57
|
|
51
|
-
synchronize do
|
58
|
+
acquired = synchronize do
|
52
59
|
if timeout.nil?
|
53
60
|
try_acquire_now(permits)
|
54
61
|
else
|
55
62
|
try_acquire_timed(permits, timeout)
|
56
63
|
end
|
57
64
|
end
|
65
|
+
|
66
|
+
return acquired unless block_given?
|
67
|
+
return unless acquired
|
68
|
+
|
69
|
+
begin
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
release(permits)
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
76
|
# @!macro semaphore_method_release
|
@@ -267,12 +267,10 @@ module Concurrent
|
|
267
267
|
# running right now, AND no writers who came before us still waiting to
|
268
268
|
# acquire the lock
|
269
269
|
# Additionally, if any read locks have been taken, we must hold all of them
|
270
|
-
if
|
271
|
-
# If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
|
272
|
-
|
273
|
-
|
274
|
-
return true
|
275
|
-
end
|
270
|
+
if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
|
271
|
+
# If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
|
272
|
+
@HeldCount.value = held + WRITE_LOCK_HELD
|
273
|
+
return true
|
276
274
|
elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
|
277
275
|
while true
|
278
276
|
# Now we have successfully incremented, so no more readers will be able to increment
|
@@ -28,38 +28,27 @@ module Concurrent
|
|
28
28
|
# But when a Thread is GC'd, we need to drop the reference to its thread-local
|
29
29
|
# array, so we don't leak memory
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
ARRAYS.each_value do |array|
|
49
|
-
array[i] = nil
|
50
|
-
end
|
51
|
-
end
|
52
|
-
when :thread_finalizer
|
53
|
-
LOCK.synchronize do
|
54
|
-
# The thread which used this thread-local array is now gone
|
55
|
-
# So don't hold onto a reference to the array (thus blocking GC)
|
56
|
-
ARRAYS.delete(i)
|
57
|
-
end
|
58
|
-
end
|
31
|
+
FREE = []
|
32
|
+
LOCK = Mutex.new
|
33
|
+
THREAD_LOCAL_ARRAYS = {} # used as a hash set
|
34
|
+
|
35
|
+
# synchronize when not on MRI
|
36
|
+
# on MRI using lock in finalizer leads to "can't be called from trap context" error
|
37
|
+
# so the code is carefully written to be tread-safe on MRI relying on GIL
|
38
|
+
|
39
|
+
if Concurrent.on_cruby?
|
40
|
+
# @!visibility private
|
41
|
+
def self.semi_sync(&block)
|
42
|
+
block.call
|
43
|
+
end
|
44
|
+
else
|
45
|
+
# @!visibility private
|
46
|
+
def self.semi_sync(&block)
|
47
|
+
LOCK.synchronize(&block)
|
59
48
|
end
|
60
49
|
end
|
61
50
|
|
62
|
-
private_constant :FREE, :LOCK, :
|
51
|
+
private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS
|
63
52
|
|
64
53
|
# @!macro thread_local_var_method_get
|
65
54
|
def value
|
@@ -85,7 +74,7 @@ module Concurrent
|
|
85
74
|
# Using Ruby's built-in thread-local storage is faster
|
86
75
|
unless (array = get_threadlocal_array(me))
|
87
76
|
array = set_threadlocal_array([], me)
|
88
|
-
|
77
|
+
self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array }
|
89
78
|
ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id))
|
90
79
|
end
|
91
80
|
array[@index] = (value.nil? ? NULL : value)
|
@@ -95,32 +84,53 @@ module Concurrent
|
|
95
84
|
protected
|
96
85
|
|
97
86
|
# @!visibility private
|
98
|
-
# noinspection RubyClassVariableUsageInspection
|
99
87
|
def allocate_storage
|
100
|
-
@index =
|
101
|
-
|
102
|
-
result = @@next
|
103
|
-
@@next += 1
|
104
|
-
result
|
105
|
-
end
|
106
|
-
end
|
88
|
+
@index = FREE.pop || next_index
|
89
|
+
|
107
90
|
ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index))
|
108
91
|
end
|
109
92
|
|
110
93
|
# @!visibility private
|
111
94
|
def self.thread_local_finalizer(index)
|
112
|
-
|
113
|
-
|
95
|
+
proc do
|
96
|
+
semi_sync do
|
97
|
+
# The cost of GC'ing a TLV is linear in the number of threads using TLVs
|
98
|
+
# But that is natural! More threads means more storage is used per TLV
|
99
|
+
# So naturally more CPU time is required to free more storage
|
100
|
+
#
|
101
|
+
# DO NOT use each_value which might conflict with new pair assignment
|
102
|
+
# into the hash in #value= method
|
103
|
+
THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil }
|
104
|
+
# free index has to be published after the arrays are cleared
|
105
|
+
FREE.push(index)
|
106
|
+
end
|
107
|
+
end
|
114
108
|
end
|
115
109
|
|
116
110
|
# @!visibility private
|
117
111
|
def self.thread_finalizer(id)
|
118
|
-
|
119
|
-
|
112
|
+
proc do
|
113
|
+
semi_sync do
|
114
|
+
# The thread which used this thread-local array is now gone
|
115
|
+
# So don't hold onto a reference to the array (thus blocking GC)
|
116
|
+
THREAD_LOCAL_ARRAYS.delete(id)
|
117
|
+
end
|
118
|
+
end
|
120
119
|
end
|
121
120
|
|
122
121
|
private
|
123
122
|
|
123
|
+
# noinspection RubyClassVariableUsageInspection
|
124
|
+
@@next = 0
|
125
|
+
# noinspection RubyClassVariableUsageInspection
|
126
|
+
def next_index
|
127
|
+
LOCK.synchronize do
|
128
|
+
result = @@next
|
129
|
+
@@next += 1
|
130
|
+
result
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
124
134
|
if Thread.instance_methods.include?(:thread_variable_get)
|
125
135
|
|
126
136
|
def get_threadlocal_array(thread = Thread.current)
|
@@ -16,14 +16,16 @@ module Concurrent
|
|
16
16
|
# @!macro semaphore_method_acquire
|
17
17
|
#
|
18
18
|
# Acquires the given number of permits from this semaphore,
|
19
|
-
# blocking until all are available.
|
19
|
+
# blocking until all are available. If a block is given,
|
20
|
+
# yields to it and releases the permits afterwards.
|
20
21
|
#
|
21
22
|
# @param [Fixnum] permits Number of permits to acquire
|
22
23
|
#
|
23
24
|
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
24
25
|
# one
|
25
26
|
#
|
26
|
-
# @return [nil]
|
27
|
+
# @return [nil, BasicObject] Without a block, `nil` is returned. If a block
|
28
|
+
# is given, its return value is returned.
|
27
29
|
|
28
30
|
# @!macro semaphore_method_available_permits
|
29
31
|
#
|
@@ -41,7 +43,9 @@ module Concurrent
|
|
41
43
|
#
|
42
44
|
# Acquires the given number of permits from this semaphore,
|
43
45
|
# only if all are available at the time of invocation or within
|
44
|
-
# `timeout` interval
|
46
|
+
# `timeout` interval. If a block is given, yields to it if the permits
|
47
|
+
# were successfully acquired, and releases them afterward, returning the
|
48
|
+
# block's return value.
|
45
49
|
#
|
46
50
|
# @param [Fixnum] permits the number of permits to acquire
|
47
51
|
#
|
@@ -51,8 +55,10 @@ module Concurrent
|
|
51
55
|
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
52
56
|
# one
|
53
57
|
#
|
54
|
-
# @return [
|
55
|
-
# acquired a permit
|
58
|
+
# @return [true, false, nil, BasicObject] `false` if no permits are
|
59
|
+
# available, `true` when acquired a permit. If a block is given, the
|
60
|
+
# block's return value is returned if the permits were acquired; if not,
|
61
|
+
# `nil` is returned.
|
56
62
|
|
57
63
|
# @!macro semaphore_method_release
|
58
64
|
#
|
@@ -106,6 +112,8 @@ module Concurrent
|
|
106
112
|
# releasing a blocking acquirer.
|
107
113
|
# However, no actual permit objects are used; the Semaphore just keeps a
|
108
114
|
# count of the number available and acts accordingly.
|
115
|
+
# Alternatively, permits may be acquired within a block, and automatically
|
116
|
+
# released after the block finishes executing.
|
109
117
|
#
|
110
118
|
# @!macro semaphore_public_api
|
111
119
|
# @example
|
@@ -140,6 +148,19 @@ module Concurrent
|
|
140
148
|
# # Thread 4 releasing semaphore
|
141
149
|
# # Thread 1 acquired semaphore
|
142
150
|
#
|
151
|
+
# @example
|
152
|
+
# semaphore = Concurrent::Semaphore.new(1)
|
153
|
+
#
|
154
|
+
# puts semaphore.available_permits
|
155
|
+
# semaphore.acquire do
|
156
|
+
# puts semaphore.available_permits
|
157
|
+
# end
|
158
|
+
# puts semaphore.available_permits
|
159
|
+
#
|
160
|
+
# # prints:
|
161
|
+
# # 1
|
162
|
+
# # 0
|
163
|
+
# # 1
|
143
164
|
class Semaphore < SemaphoreImplementation
|
144
165
|
end
|
145
166
|
end
|
@@ -19,7 +19,7 @@ module Concurrent
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def compute_if_absent(key)
|
22
|
-
if stored_value =
|
22
|
+
if NULL != (stored_value = @backend.fetch(key, NULL)) # fast non-blocking path for the most likely case
|
23
23
|
stored_value
|
24
24
|
else
|
25
25
|
@write_lock.synchronize { super }
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
|
8
|
+
def initialize(options = nil)
|
9
|
+
options ||= {}
|
10
|
+
super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -30,7 +30,7 @@ module Concurrent
|
|
30
30
|
if @queue[k] == item
|
31
31
|
swap(k, @length)
|
32
32
|
@length -= 1
|
33
|
-
sink(k)
|
33
|
+
sink(k) || swim(k)
|
34
34
|
@queue.pop
|
35
35
|
else
|
36
36
|
k += 1
|
@@ -126,12 +126,17 @@ module Concurrent
|
|
126
126
|
#
|
127
127
|
# @!visibility private
|
128
128
|
def sink(k)
|
129
|
+
success = false
|
130
|
+
|
129
131
|
while (j = (2 * k)) <= @length do
|
130
132
|
j += 1 if j < @length && ! ordered?(j, j+1)
|
131
133
|
break if ordered?(k, j)
|
132
134
|
swap(k, j)
|
135
|
+
success = true
|
133
136
|
k = j
|
134
137
|
end
|
138
|
+
|
139
|
+
success
|
135
140
|
end
|
136
141
|
|
137
142
|
# Percolate up to maintain heap invariant.
|
@@ -140,10 +145,15 @@ module Concurrent
|
|
140
145
|
#
|
141
146
|
# @!visibility private
|
142
147
|
def swim(k)
|
148
|
+
success = false
|
149
|
+
|
143
150
|
while k > 1 && ! ordered?(k/2, k) do
|
144
151
|
swap(k, k/2)
|
145
152
|
k = k/2
|
153
|
+
success = true
|
146
154
|
end
|
155
|
+
|
156
|
+
success
|
147
157
|
end
|
148
158
|
end
|
149
159
|
end
|
Binary file
|
@@ -75,28 +75,31 @@ module Concurrent
|
|
75
75
|
|
76
76
|
private
|
77
77
|
|
78
|
-
#
|
79
|
-
# reaches `max_queue`.
|
78
|
+
# Returns an action which executes the `fallback_policy` once the queue
|
79
|
+
# size reaches `max_queue`. The reason for the indirection of an action
|
80
|
+
# is so that the work can be deferred outside of synchronization.
|
80
81
|
#
|
81
82
|
# @param [Array] args the arguments to the task which is being handled.
|
82
83
|
#
|
83
84
|
# @!visibility private
|
84
|
-
def
|
85
|
+
def fallback_action(*args)
|
85
86
|
case fallback_policy
|
86
87
|
when :abort
|
87
|
-
raise RejectedExecutionError
|
88
|
+
lambda { raise RejectedExecutionError }
|
88
89
|
when :discard
|
89
|
-
false
|
90
|
+
lambda { false }
|
90
91
|
when :caller_runs
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
lambda {
|
93
|
+
begin
|
94
|
+
yield(*args)
|
95
|
+
rescue => ex
|
96
|
+
# let it fail
|
97
|
+
log DEBUG, ex
|
98
|
+
end
|
99
|
+
true
|
100
|
+
}
|
98
101
|
else
|
99
|
-
fail "Unknown fallback policy #{fallback_policy}"
|
102
|
+
lambda { fail "Unknown fallback policy #{fallback_policy}" }
|
100
103
|
end
|
101
104
|
end
|
102
105
|
|
@@ -16,6 +16,9 @@ module Concurrent
|
|
16
16
|
# Default maximum number of seconds a thread in the pool may remain idle
|
17
17
|
# before being reclaimed.
|
18
18
|
|
19
|
+
# @!macro thread_pool_executor_constant_default_synchronous
|
20
|
+
# Default value of the :synchronous option.
|
21
|
+
|
19
22
|
# @!macro thread_pool_executor_attr_reader_max_length
|
20
23
|
# The maximum number of threads that may be created in the pool.
|
21
24
|
# @return [Integer] The maximum number of threads that may be created in the pool.
|
@@ -40,6 +43,10 @@ module Concurrent
|
|
40
43
|
# The number of seconds that a thread may be idle before being reclaimed.
|
41
44
|
# @return [Integer] The number of seconds that a thread may be idle before being reclaimed.
|
42
45
|
|
46
|
+
# @!macro thread_pool_executor_attr_reader_synchronous
|
47
|
+
# Whether or not a value of 0 for :max_queue option means the queue must perform direct hand-off or rather unbounded queue.
|
48
|
+
# @return [true, false]
|
49
|
+
|
43
50
|
# @!macro thread_pool_executor_attr_reader_max_queue
|
44
51
|
# The maximum number of tasks that may be waiting in the work queue at any one time.
|
45
52
|
# When the queue size reaches `max_queue` subsequent tasks will be rejected in
|
@@ -64,9 +71,16 @@ module Concurrent
|
|
64
71
|
# @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting
|
65
72
|
# new tasks. A value of -1 indicates that the queue may grow without bound.
|
66
73
|
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
# @!macro thread_pool_executor_method_prune_pool
|
75
|
+
# Prune the thread pool of unneeded threads
|
76
|
+
#
|
77
|
+
# What is being pruned is controlled by the min_threads and idletime
|
78
|
+
# parameters passed at pool creation time
|
79
|
+
#
|
80
|
+
# This is a no-op on some pool implementation (e.g. the Java one). The Ruby
|
81
|
+
# pool will auto-prune each time a new job is posted. You will need to call
|
82
|
+
# this method explicitely in case your application post jobs in bursts (a
|
83
|
+
# lot of jobs and then nothing for long periods)
|
70
84
|
|
71
85
|
# @!macro thread_pool_executor_public_api
|
72
86
|
#
|
@@ -104,6 +118,9 @@ module Concurrent
|
|
104
118
|
#
|
105
119
|
# @!method can_overflow?
|
106
120
|
# @!macro executor_service_method_can_overflow_question
|
121
|
+
#
|
122
|
+
# @!method prune_pool
|
123
|
+
# @!macro thread_pool_executor_method_prune_pool
|
107
124
|
|
108
125
|
|
109
126
|
|
@@ -20,7 +20,7 @@ if Concurrent.on_jruby?
|
|
20
20
|
|
21
21
|
def post(*args, &task)
|
22
22
|
raise ArgumentError.new('no block given') unless block_given?
|
23
|
-
return
|
23
|
+
return fallback_action(*args, &task).call unless running?
|
24
24
|
@executor.submit Job.new(args, task)
|
25
25
|
true
|
26
26
|
rescue Java::JavaUtilConcurrent::RejectedExecutionException
|
@@ -21,12 +21,18 @@ if Concurrent.on_jruby?
|
|
21
21
|
# @!macro thread_pool_executor_constant_default_thread_timeout
|
22
22
|
DEFAULT_THREAD_IDLETIMEOUT = 60
|
23
23
|
|
24
|
+
# @!macro thread_pool_executor_constant_default_synchronous
|
25
|
+
DEFAULT_SYNCHRONOUS = false
|
26
|
+
|
24
27
|
# @!macro thread_pool_executor_attr_reader_max_length
|
25
28
|
attr_reader :max_length
|
26
29
|
|
27
30
|
# @!macro thread_pool_executor_attr_reader_max_queue
|
28
31
|
attr_reader :max_queue
|
29
32
|
|
33
|
+
# @!macro thread_pool_executor_attr_reader_synchronous
|
34
|
+
attr_reader :synchronous
|
35
|
+
|
30
36
|
# @!macro thread_pool_executor_method_initialize
|
31
37
|
def initialize(opts = {})
|
32
38
|
super(opts)
|
@@ -87,6 +93,10 @@ if Concurrent.on_jruby?
|
|
87
93
|
super && !@executor.isTerminating
|
88
94
|
end
|
89
95
|
|
96
|
+
# @!macro thread_pool_executor_method_prune_pool
|
97
|
+
def prune_pool
|
98
|
+
end
|
99
|
+
|
90
100
|
private
|
91
101
|
|
92
102
|
def ns_initialize(opts)
|
@@ -94,8 +104,10 @@ if Concurrent.on_jruby?
|
|
94
104
|
max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
|
95
105
|
idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
|
96
106
|
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
|
107
|
+
@synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
|
97
108
|
@fallback_policy = opts.fetch(:fallback_policy, :abort)
|
98
109
|
|
110
|
+
raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
|
99
111
|
raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE
|
100
112
|
raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE
|
101
113
|
raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE
|
@@ -103,7 +115,11 @@ if Concurrent.on_jruby?
|
|
103
115
|
raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy)
|
104
116
|
|
105
117
|
if @max_queue == 0
|
106
|
-
|
118
|
+
if @synchronous
|
119
|
+
queue = java.util.concurrent.SynchronousQueue.new
|
120
|
+
else
|
121
|
+
queue = java.util.concurrent.LinkedBlockingQueue.new
|
122
|
+
end
|
107
123
|
else
|
108
124
|
queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)
|
109
125
|
end
|
@@ -16,10 +16,16 @@ module Concurrent
|
|
16
16
|
|
17
17
|
def post(*args, &task)
|
18
18
|
raise ArgumentError.new('no block given') unless block_given?
|
19
|
-
synchronize
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
deferred_action = synchronize {
|
20
|
+
if running?
|
21
|
+
ns_execute(*args, &task)
|
22
|
+
else
|
23
|
+
fallback_action(*args, &task)
|
24
|
+
end
|
25
|
+
}
|
26
|
+
if deferred_action
|
27
|
+
deferred_action.call
|
28
|
+
else
|
23
29
|
true
|
24
30
|
end
|
25
31
|
end
|