concurrent-ruby 0.9.2-java → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -1
- data/README.md +86 -120
- data/lib/concurrent.rb +14 -5
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +39 -0
- data/lib/concurrent/async.rb +296 -149
- data/lib/concurrent/atom.rb +135 -45
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
- data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
- data/lib/concurrent/atomic/atomic_reference.rb +1 -8
- data/lib/concurrent/atomic/count_down_latch.rb +62 -103
- data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- data/lib/concurrent/atomic/read_write_lock.rb +5 -4
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +172 -0
- data/lib/concurrent/atomic/semaphore.rb +84 -178
- data/lib/concurrent/atomic/thread_local_var.rb +65 -294
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
- data/lib/concurrent/atomic_reference/jruby.rb +1 -1
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
- data/lib/concurrent/atomic_reference/ruby.rb +1 -1
- data/lib/concurrent/atomics.rb +7 -37
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
- data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/dereferenceable.rb +9 -24
- data/lib/concurrent/concern/logging.rb +1 -1
- data/lib/concurrent/concern/obligation.rb +11 -20
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +23 -152
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/delay.rb +14 -12
- data/lib/concurrent/exchanger.rb +339 -41
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/executor_service.rb +23 -359
- data/lib/concurrent/executor/immediate_executor.rb +3 -2
- data/lib/concurrent/executor/java_executor_service.rb +100 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
- data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
- data/lib/concurrent/executor/safe_task_executor.rb +6 -7
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +10 -33
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +1 -10
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +8 -10
- data/lib/concurrent/executors.rb +12 -2
- data/lib/concurrent/future.rb +6 -4
- data/lib/concurrent/hash.rb +35 -0
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +12 -16
- data/lib/concurrent/lazy_register.rb +11 -8
- data/lib/concurrent/map.rb +180 -0
- data/lib/concurrent/maybe.rb +6 -3
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/mvar.rb +26 -2
- data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
- data/lib/concurrent/promise.rb +7 -5
- data/lib/concurrent/scheduled_task.rb +13 -71
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +15 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +7 -146
- data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
- data/lib/concurrent/synchronization/condition.rb +6 -4
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/jruby_object.rb +44 -0
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +72 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +140 -73
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +30 -73
- data/lib/concurrent/synchronization/volatile.rb +34 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +14 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +3 -4
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +5 -1
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/engine.rb +4 -0
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +50 -30
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +47 -12
- data/lib/concurrent/atomic/condition.rb +0 -78
- data/lib/concurrent/collection/priority_queue.rb +0 -360
- data/lib/concurrent/synchronization/java_object.rb +0 -34
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- data/lib/concurrent/synchronization/mutex_object.rb +0 -43
- data/lib/concurrent/utilities.rb +0 -5
- data/lib/concurrent/utility/timeout.rb +0 -39
- data/lib/concurrent/utility/timer.rb +0 -26
- data/lib/concurrent_ruby.rb +0 -2
@@ -7,19 +7,22 @@ module Concurrent
|
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# register = Concurrent::LazyRegister.new
|
10
|
-
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @
|
10
|
+
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @Data=#<Concurrent::AtomicReference:0x007fd7ecd5e1e0>>
|
11
11
|
# register[:key]
|
12
12
|
# #=> nil
|
13
13
|
# register.add(:key) { Concurrent::Actor.spawn!(Actor::AdHoc, :ping) { -> message { message } } }
|
14
|
-
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @
|
14
|
+
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @Data=#<Concurrent::AtomicReference:0x007fd7ecd5e1e0>>
|
15
15
|
# register[:key]
|
16
16
|
# #=> #<Concurrent::Actor::Reference /ping (Concurrent::Actor::AdHoc)>
|
17
17
|
#
|
18
18
|
# @!macro edge_warning
|
19
|
-
class LazyRegister
|
19
|
+
class LazyRegister < Synchronization::Object
|
20
|
+
|
21
|
+
private(*attr_atomic(:data))
|
20
22
|
|
21
23
|
def initialize
|
22
|
-
|
24
|
+
super
|
25
|
+
self.data = {}
|
23
26
|
end
|
24
27
|
|
25
28
|
# Element reference. Retrieves the value object corresponding to the
|
@@ -31,7 +34,7 @@ module Concurrent
|
|
31
34
|
#
|
32
35
|
# @raise Exception when the initialization block fails
|
33
36
|
def [](key)
|
34
|
-
delay =
|
37
|
+
delay = data[key]
|
35
38
|
delay ? delay.value! : nil
|
36
39
|
end
|
37
40
|
|
@@ -40,7 +43,7 @@ module Concurrent
|
|
40
43
|
# @param [Object] key
|
41
44
|
# @return [true, false] if the key is registered
|
42
45
|
def registered?(key)
|
43
|
-
|
46
|
+
data.key?(key)
|
44
47
|
end
|
45
48
|
|
46
49
|
alias_method :key?, :registered?
|
@@ -55,7 +58,7 @@ module Concurrent
|
|
55
58
|
# @return [LazyRegister] self
|
56
59
|
def register(key, &block)
|
57
60
|
delay = Delay.new(executor: :immediate, &block)
|
58
|
-
|
61
|
+
update_data { |h| h.merge(key => delay) }
|
59
62
|
self
|
60
63
|
end
|
61
64
|
|
@@ -68,7 +71,7 @@ module Concurrent
|
|
68
71
|
#
|
69
72
|
# @return [LazyRegister] self
|
70
73
|
def unregister(key)
|
71
|
-
|
74
|
+
update_data { |h| h.dup.tap { |j| j.delete(key) } }
|
72
75
|
self
|
73
76
|
end
|
74
77
|
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'concurrent/constants'
|
3
|
+
require 'concurrent/synchronization'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
# @!visibility private
|
7
|
+
module Collection
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
MapImplementation = if Concurrent.java_extensions_loaded?
|
11
|
+
# noinspection RubyResolve
|
12
|
+
JRubyMapBackend
|
13
|
+
elsif defined?(RUBY_ENGINE)
|
14
|
+
case RUBY_ENGINE
|
15
|
+
when 'ruby'
|
16
|
+
require 'concurrent/collection/map/mri_map_backend'
|
17
|
+
MriMapBackend
|
18
|
+
when 'rbx'
|
19
|
+
require 'concurrent/collection/map/atomic_reference_map_backend'
|
20
|
+
AtomicReferenceMapBackend
|
21
|
+
else
|
22
|
+
warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation' if $VERBOSE
|
23
|
+
require 'concurrent/collection/map/synchronized_map_backend'
|
24
|
+
SynchronizedMapBackend
|
25
|
+
end
|
26
|
+
else
|
27
|
+
MriMapBackend
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# `Concurrent::Map` is a hash-like object and should have much better performance
|
32
|
+
# characteristics, especially under high concurrency, than `Concurrent::Hash`.
|
33
|
+
# However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash`
|
34
|
+
# -- for instance, it does not necessarily retain ordering by insertion time as `Hash`
|
35
|
+
# does. For most uses it should do fine though, and we recommend you consider
|
36
|
+
# `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.
|
37
|
+
#
|
38
|
+
# > require 'concurrent'
|
39
|
+
# >
|
40
|
+
# > map = Concurrent::Map.new
|
41
|
+
|
42
|
+
class Map < Collection::MapImplementation
|
43
|
+
def initialize(options = nil, &block)
|
44
|
+
if options.kind_of?(::Hash)
|
45
|
+
validate_options_hash!(options)
|
46
|
+
else
|
47
|
+
options = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
super(options)
|
51
|
+
@default_proc = block
|
52
|
+
end
|
53
|
+
|
54
|
+
def [](key)
|
55
|
+
if value = super # non-falsy value is an existing mapping, return it right away
|
56
|
+
value
|
57
|
+
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
|
58
|
+
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
|
59
|
+
# would be returned)
|
60
|
+
# note: nil == value check is not technically necessary
|
61
|
+
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
|
62
|
+
@default_proc.call(self, key)
|
63
|
+
else
|
64
|
+
value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
alias_method :get, :[]
|
69
|
+
alias_method :put, :[]=
|
70
|
+
|
71
|
+
def fetch(key, default_value = NULL)
|
72
|
+
if NULL != (value = get_or_default(key, NULL))
|
73
|
+
value
|
74
|
+
elsif block_given?
|
75
|
+
yield key
|
76
|
+
elsif NULL != default_value
|
77
|
+
default_value
|
78
|
+
else
|
79
|
+
raise_fetch_no_key
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def fetch_or_store(key, default_value = NULL)
|
84
|
+
fetch(key) do
|
85
|
+
put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def put_if_absent(key, value)
|
90
|
+
computed = false
|
91
|
+
result = compute_if_absent(key) do
|
92
|
+
computed = true
|
93
|
+
value
|
94
|
+
end
|
95
|
+
computed ? nil : result
|
96
|
+
end unless method_defined?(:put_if_absent)
|
97
|
+
|
98
|
+
def value?(value)
|
99
|
+
each_value do |v|
|
100
|
+
return true if value.equal?(v)
|
101
|
+
end
|
102
|
+
false
|
103
|
+
end unless method_defined?(:value?)
|
104
|
+
|
105
|
+
def keys
|
106
|
+
arr = []
|
107
|
+
each_pair {|k, v| arr << k}
|
108
|
+
arr
|
109
|
+
end unless method_defined?(:keys)
|
110
|
+
|
111
|
+
def values
|
112
|
+
arr = []
|
113
|
+
each_pair {|k, v| arr << v}
|
114
|
+
arr
|
115
|
+
end unless method_defined?(:values)
|
116
|
+
|
117
|
+
def each_key
|
118
|
+
each_pair {|k, v| yield k}
|
119
|
+
end unless method_defined?(:each_key)
|
120
|
+
|
121
|
+
def each_value
|
122
|
+
each_pair {|k, v| yield v}
|
123
|
+
end unless method_defined?(:each_value)
|
124
|
+
|
125
|
+
def key(value)
|
126
|
+
each_pair {|k, v| return k if v == value}
|
127
|
+
nil
|
128
|
+
end unless method_defined?(:key)
|
129
|
+
alias_method :index, :key if RUBY_VERSION < '1.9'
|
130
|
+
|
131
|
+
def empty?
|
132
|
+
each_pair {|k, v| return false}
|
133
|
+
true
|
134
|
+
end unless method_defined?(:empty?)
|
135
|
+
|
136
|
+
def size
|
137
|
+
count = 0
|
138
|
+
each_pair {|k, v| count += 1}
|
139
|
+
count
|
140
|
+
end unless method_defined?(:size)
|
141
|
+
|
142
|
+
def marshal_dump
|
143
|
+
raise TypeError, "can't dump hash with default proc" if @default_proc
|
144
|
+
h = {}
|
145
|
+
each_pair {|k, v| h[k] = v}
|
146
|
+
h
|
147
|
+
end
|
148
|
+
|
149
|
+
def marshal_load(hash)
|
150
|
+
initialize
|
151
|
+
populate_from(hash)
|
152
|
+
end
|
153
|
+
|
154
|
+
undef :freeze
|
155
|
+
|
156
|
+
private
|
157
|
+
def raise_fetch_no_key
|
158
|
+
raise KeyError, 'key not found'
|
159
|
+
end
|
160
|
+
|
161
|
+
def initialize_copy(other)
|
162
|
+
super
|
163
|
+
populate_from(other)
|
164
|
+
end
|
165
|
+
|
166
|
+
def populate_from(hash)
|
167
|
+
hash.each_pair {|k, v| self[k] = v}
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
def validate_options_hash!(options)
|
172
|
+
if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Fixnum) || initial_capacity < 0)
|
173
|
+
raise ArgumentError, ":initial_capacity must be a positive Fixnum"
|
174
|
+
end
|
175
|
+
if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
|
176
|
+
raise ArgumentError, ":load_factor must be a number between 0 and 1"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/lib/concurrent/maybe.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'concurrent/synchronization'
|
2
|
+
|
1
3
|
module Concurrent
|
2
4
|
|
3
5
|
# A `Maybe` encapsulates an optional value. A `Maybe` either contains a value
|
@@ -99,8 +101,9 @@ module Concurrent
|
|
99
101
|
#
|
100
102
|
# @see https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html Haskell Data.Maybe
|
101
103
|
# @see https://github.com/purescript/purescript-maybe/blob/master/docs/Data.Maybe.md PureScript Data.Maybe
|
102
|
-
class Maybe
|
104
|
+
class Maybe < Synchronization::Object
|
103
105
|
include Comparable
|
106
|
+
safe_initialization!
|
104
107
|
|
105
108
|
# Indicates that the given attribute has not been set.
|
106
109
|
# When `Just` the {#nothing} getter will return `NONE`.
|
@@ -168,7 +171,7 @@ module Concurrent
|
|
168
171
|
end
|
169
172
|
|
170
173
|
# Is this `Maybe` a `Just` (successfully fulfilled with a value)?
|
171
|
-
#
|
174
|
+
#
|
172
175
|
# @return [Boolean] True if `Just` or false if `Nothing`.
|
173
176
|
def just?
|
174
177
|
! nothing?
|
@@ -176,7 +179,7 @@ module Concurrent
|
|
176
179
|
alias :fulfilled? :just?
|
177
180
|
|
178
181
|
# Is this `Maybe` a `nothing` (rejected with an exception upon fulfillment)?
|
179
|
-
#
|
182
|
+
#
|
180
183
|
# @return [Boolean] True if `Nothing` or false if `Just`.
|
181
184
|
def nothing?
|
182
185
|
@nothing != NONE
|
@@ -75,7 +75,7 @@ module Concurrent
|
|
75
75
|
alias_method :to_s, :inspect
|
76
76
|
|
77
77
|
# @!macro [attach] struct_merge
|
78
|
-
#
|
78
|
+
#
|
79
79
|
# Returns a new struct containing the contents of `other` and the contents
|
80
80
|
# of `self`. If no block is specified, the value for entries with duplicate
|
81
81
|
# keys will be that of `other`. Otherwise the value for each duplicate key
|
@@ -98,7 +98,7 @@ module Concurrent
|
|
98
98
|
# @!macro [attach] struct_to_h
|
99
99
|
#
|
100
100
|
# Returns a hash containing the names and values for the struct’s members.
|
101
|
-
#
|
101
|
+
#
|
102
102
|
# @return [Hash] the names and values for the struct’s members
|
103
103
|
def to_h
|
104
104
|
synchronize { ns_to_h }
|
@@ -184,8 +184,9 @@ module Concurrent
|
|
184
184
|
# @raise [IndexError] if the index is out of range.
|
185
185
|
def []=(member, value)
|
186
186
|
if member.is_a? Integer
|
187
|
-
|
188
|
-
|
187
|
+
length = synchronize { @values.length }
|
188
|
+
if member >= length
|
189
|
+
raise IndexError.new("offset #{member} too large for struct(size:#{length})")
|
189
190
|
end
|
190
191
|
synchronize { @values[member] = value }
|
191
192
|
else
|
@@ -206,10 +207,10 @@ module Concurrent
|
|
206
207
|
FACTORY.define_struct(clazz_name, args, &block)
|
207
208
|
end
|
208
209
|
|
209
|
-
FACTORY = Class.new(Synchronization::
|
210
|
+
FACTORY = Class.new(Synchronization::LockableObject) do
|
210
211
|
def define_struct(name, members, &block)
|
211
212
|
synchronize do
|
212
|
-
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::
|
213
|
+
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block)
|
213
214
|
members.each_with_index do |member, index|
|
214
215
|
clazz.send(:define_method, member) do
|
215
216
|
synchronize { @values[index] }
|
data/lib/concurrent/mvar.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'concurrent/concern/dereferenceable'
|
2
|
+
require 'concurrent/synchronization'
|
2
3
|
|
3
4
|
module Concurrent
|
4
5
|
|
@@ -34,9 +35,9 @@ module Concurrent
|
|
34
35
|
# 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794).
|
35
36
|
# In Proceedings of the 23rd Symposium on Principles of Programming Languages
|
36
37
|
# (PoPL), 1996.
|
37
|
-
class MVar
|
38
|
-
|
38
|
+
class MVar < Synchronization::Object
|
39
39
|
include Concern::Dereferenceable
|
40
|
+
safe_initialization!
|
40
41
|
|
41
42
|
# Unique value that represents that an `MVar` was empty
|
42
43
|
EMPTY = Object.new
|
@@ -78,6 +79,23 @@ module Concurrent
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
82
|
+
# acquires lock on the from an `MVAR`, yields the value to provided block,
|
83
|
+
# and release lock. A timeout can be set to limit the time spent blocked,
|
84
|
+
# in which case it returns `TIMEOUT` if the time is exceeded.
|
85
|
+
# @return [Object] the value returned by the block, or `TIMEOUT`
|
86
|
+
def borrow(timeout = nil)
|
87
|
+
@mutex.synchronize do
|
88
|
+
wait_for_full(timeout)
|
89
|
+
|
90
|
+
# if we timeoud out we'll still be empty
|
91
|
+
if unlocked_full?
|
92
|
+
yield @value
|
93
|
+
else
|
94
|
+
TIMEOUT
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
81
99
|
# Put a value into an `MVar`, blocking if there is already a value until
|
82
100
|
# it is empty. A timeout can be set to limit the time spent blocked, in
|
83
101
|
# which case it returns `TIMEOUT` if the time is exceeded.
|
@@ -183,6 +201,12 @@ module Concurrent
|
|
183
201
|
!empty?
|
184
202
|
end
|
185
203
|
|
204
|
+
protected
|
205
|
+
|
206
|
+
def synchronize(&block)
|
207
|
+
@mutex.synchronize(&block)
|
208
|
+
end
|
209
|
+
|
186
210
|
private
|
187
211
|
|
188
212
|
def unlocked_empty?
|
@@ -1,12 +1,11 @@
|
|
1
|
+
# This file has circular require issues. It must be autoloaded.
|
2
|
+
|
1
3
|
require 'concurrent/configuration'
|
2
|
-
require 'concurrent/concern/deprecation'
|
3
|
-
require 'concurrent/executor/executor_service'
|
4
4
|
|
5
5
|
module Concurrent
|
6
6
|
|
7
7
|
# @!visibility private
|
8
|
-
module
|
9
|
-
extend Concern::Deprecation
|
8
|
+
module Options
|
10
9
|
|
11
10
|
# Get the requested `Executor` based on the values set in the options hash.
|
12
11
|
#
|
@@ -20,25 +19,8 @@ module Concurrent
|
|
20
19
|
#
|
21
20
|
# @!visibility private
|
22
21
|
def self.executor_from_options(opts = {}) # :nodoc:
|
23
|
-
|
24
|
-
|
25
|
-
if opts[:executor].nil?
|
26
|
-
nil
|
27
|
-
else
|
28
|
-
executor(opts[:executor])
|
29
|
-
end
|
30
|
-
when opts.key?(:operation) || opts.key?(:task)
|
31
|
-
if opts[:operation] == true || opts[:task] == false
|
32
|
-
deprecated 'use `executor: :fast` instead'
|
33
|
-
return Concurrent.global_fast_executor
|
34
|
-
end
|
35
|
-
|
36
|
-
if opts[:operation] == false || opts[:task] == true
|
37
|
-
deprecated 'use `executor: :io` instead'
|
38
|
-
return Concurrent.global_io_executor
|
39
|
-
end
|
40
|
-
|
41
|
-
raise ArgumentError.new("executor '#{opts[:executor]}' not recognized")
|
22
|
+
if identifier = opts.fetch(:executor, nil)
|
23
|
+
executor(identifier)
|
42
24
|
else
|
43
25
|
nil
|
44
26
|
end
|
@@ -52,12 +34,6 @@ module Concurrent
|
|
52
34
|
Concurrent.global_io_executor
|
53
35
|
when :immediate
|
54
36
|
Concurrent.global_immediate_executor
|
55
|
-
when :operation
|
56
|
-
deprecated 'use `executor: :fast` instead'
|
57
|
-
Concurrent.global_fast_executor
|
58
|
-
when :task
|
59
|
-
deprecated 'use `executor: :io` instead'
|
60
|
-
Concurrent.global_io_executor
|
61
37
|
when Concurrent::ExecutorService
|
62
38
|
executor_identifier
|
63
39
|
else
|
data/lib/concurrent/promise.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'concurrent/constants'
|
2
3
|
require 'concurrent/errors'
|
3
4
|
require 'concurrent/ivar'
|
4
|
-
require 'concurrent/executor/executor'
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
8
|
+
autoload :Options, 'concurrent/options'
|
9
|
+
|
8
10
|
PromiseExecutionError = Class.new(StandardError)
|
9
11
|
|
10
12
|
# Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
|
@@ -205,7 +207,7 @@ module Concurrent
|
|
205
207
|
# @see http://promises-aplus.github.io/promises-spec/
|
206
208
|
def initialize(opts = {}, &block)
|
207
209
|
opts.delete_if { |k, v| v.nil? }
|
208
|
-
super(
|
210
|
+
super(NULL, opts.merge(__promise_body_from_block__: block), &nil)
|
209
211
|
end
|
210
212
|
|
211
213
|
# Create a new `Promise` and fulfill it immediately.
|
@@ -254,7 +256,7 @@ module Concurrent
|
|
254
256
|
# @!macro ivar_set_method
|
255
257
|
#
|
256
258
|
# @raise [Concurrent::PromiseExecutionError] if not the root promise
|
257
|
-
def set(value =
|
259
|
+
def set(value = NULL, &block)
|
258
260
|
raise PromiseExecutionError.new('supported only on root promise') unless root?
|
259
261
|
check_for_block_or_value!(block_given?, value)
|
260
262
|
synchronize do
|
@@ -302,7 +304,7 @@ module Concurrent
|
|
302
304
|
# @return [Promise] the new promise
|
303
305
|
def then(rescuer = nil, &block)
|
304
306
|
raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
|
305
|
-
block = Proc.new { |result| result }
|
307
|
+
block = Proc.new { |result| result } unless block_given?
|
306
308
|
child = Promise.new(
|
307
309
|
parent: self,
|
308
310
|
executor: @executor,
|
@@ -441,7 +443,7 @@ module Concurrent
|
|
441
443
|
def ns_initialize(value, opts)
|
442
444
|
super
|
443
445
|
|
444
|
-
@executor =
|
446
|
+
@executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
|
445
447
|
@args = get_arguments_from(opts)
|
446
448
|
|
447
449
|
@parent = opts.fetch(:parent) { nil }
|