concurrent-ruby 0.6.0.pre.2 → 0.6.0
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/README.md +3 -8
- data/lib/concurrent.rb +2 -0
- data/lib/concurrent/actor/actor.rb +3 -3
- data/lib/concurrent/actor/postable.rb +1 -1
- data/lib/concurrent/actors.rb +0 -3
- data/lib/concurrent/actress.rb +75 -0
- data/lib/concurrent/actress/ad_hoc.rb +14 -0
- data/lib/concurrent/actress/context.rb +96 -0
- data/lib/concurrent/actress/core.rb +204 -0
- data/lib/concurrent/actress/core_delegations.rb +37 -0
- data/lib/concurrent/actress/doc.md +53 -0
- data/lib/concurrent/actress/envelope.rb +25 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +20 -11
- data/lib/concurrent/async.rb +54 -25
- data/lib/concurrent/atomic/atomic.rb +48 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
- data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
- data/lib/concurrent/atomic/event.rb +11 -16
- data/lib/concurrent/atomics.rb +1 -0
- data/lib/concurrent/channel/channel.rb +4 -2
- data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
- data/lib/concurrent/configuration.rb +59 -47
- data/lib/concurrent/delay.rb +28 -12
- data/lib/concurrent/dereferenceable.rb +6 -6
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/executor/executor.rb +11 -4
- data/lib/concurrent/executor/immediate_executor.rb +1 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent/executor/one_by_one.rb +24 -12
- data/lib/concurrent/executor/per_thread_executor.rb +1 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +0 -2
- data/lib/concurrent/ivar.rb +31 -6
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +45 -0
- data/lib/concurrent/obligation.rb +61 -20
- data/lib/concurrent/observable.rb +7 -0
- data/lib/concurrent/promise.rb +1 -0
- data/lib/concurrent/runnable.rb +2 -2
- data/lib/concurrent/supervisor.rb +1 -2
- data/lib/concurrent/timer_task.rb +17 -13
- data/lib/concurrent/tvar.rb +113 -73
- data/lib/concurrent/utility/processor_count.rb +141 -116
- data/lib/concurrent/utility/timeout.rb +4 -5
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/actor/postable_shared.rb +1 -1
- data/spec/concurrent/actress_spec.rb +191 -0
- data/spec/concurrent/async_spec.rb +35 -3
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_spec.rb +133 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
- data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
- data/spec/concurrent/configuration_spec.rb +5 -2
- data/spec/concurrent/delay_spec.rb +14 -0
- data/spec/concurrent/exchanger_spec.rb +4 -9
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
- data/spec/concurrent/ivar_spec.rb +2 -2
- data/spec/concurrent/obligation_spec.rb +113 -24
- data/spec/concurrent/observable_shared.rb +4 -0
- data/spec/concurrent/observable_spec.rb +8 -3
- data/spec/concurrent/runnable_spec.rb +2 -2
- data/spec/concurrent/scheduled_task_spec.rb +1 -0
- data/spec/concurrent/supervisor_spec.rb +26 -11
- data/spec/concurrent/timer_task_spec.rb +36 -35
- data/spec/concurrent/tvar_spec.rb +1 -1
- data/spec/concurrent/utility/timer_spec.rb +8 -8
- data/spec/spec_helper.rb +8 -18
- data/spec/support/example_group_extensions.rb +48 -0
- metadata +23 -16
- data/lib/concurrent/actor/actor_context.rb +0 -77
- data/lib/concurrent/actor/actor_ref.rb +0 -67
- data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/actor/actor_context_spec.rb +0 -29
- data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
- data/spec/support/functions.rb +0 -25
data/lib/concurrent/delay.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'concurrent/obligation'
|
2
3
|
|
3
4
|
module Concurrent
|
4
5
|
|
@@ -48,7 +49,7 @@ module Concurrent
|
|
48
49
|
|
49
50
|
init_obligation
|
50
51
|
@state = :pending
|
51
|
-
@task
|
52
|
+
@task = block
|
52
53
|
set_deref_options(opts)
|
53
54
|
end
|
54
55
|
|
@@ -73,24 +74,39 @@ module Concurrent
|
|
73
74
|
def value
|
74
75
|
mutex.lock
|
75
76
|
execute_task_once
|
76
|
-
|
77
|
+
apply_deref_options(@value)
|
78
|
+
ensure
|
77
79
|
mutex.unlock
|
80
|
+
end
|
78
81
|
|
79
|
-
|
82
|
+
# reconfigures the block returning the value if still #incomplete?
|
83
|
+
# @yield the delayed operation to perform
|
84
|
+
# @return [true, false] if success
|
85
|
+
def reconfigure(&block)
|
86
|
+
mutex.lock
|
87
|
+
raise ArgumentError.new('no block given') unless block_given?
|
88
|
+
if @state == :pending
|
89
|
+
@task = block
|
90
|
+
true
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
94
|
+
ensure
|
95
|
+
mutex.unlock
|
80
96
|
end
|
81
97
|
|
82
98
|
private
|
83
99
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
100
|
+
def execute_task_once
|
101
|
+
if @state == :pending
|
102
|
+
begin
|
103
|
+
@value = @task.call
|
104
|
+
@state = :fulfilled
|
105
|
+
rescue => ex
|
106
|
+
@reason = ex
|
107
|
+
@state = :rejected
|
93
108
|
end
|
94
109
|
end
|
110
|
+
end
|
95
111
|
end
|
96
112
|
end
|
@@ -27,9 +27,9 @@ module Concurrent
|
|
27
27
|
# @return [Object] the current value of the object
|
28
28
|
def value
|
29
29
|
mutex.lock
|
30
|
-
|
30
|
+
apply_deref_options(@value)
|
31
|
+
ensure
|
31
32
|
mutex.unlock
|
32
|
-
result
|
33
33
|
end
|
34
34
|
|
35
35
|
alias_method :deref, :value
|
@@ -41,9 +41,9 @@ module Concurrent
|
|
41
41
|
# @param [Object] val the new value
|
42
42
|
def value=(val)
|
43
43
|
mutex.lock
|
44
|
-
|
44
|
+
@value = val
|
45
|
+
ensure
|
45
46
|
mutex.unlock
|
46
|
-
result
|
47
47
|
end
|
48
48
|
|
49
49
|
# A mutex lock used for synchronizing thread-safe operations. Methods defined
|
@@ -83,15 +83,15 @@ module Concurrent
|
|
83
83
|
@freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze]
|
84
84
|
@copy_on_deref = opts[:copy_on_deref] || opts[:copy]
|
85
85
|
@do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref)
|
86
|
-
mutex.unlock
|
87
86
|
nil
|
87
|
+
ensure
|
88
|
+
mutex.unlock
|
88
89
|
end
|
89
90
|
|
90
91
|
# @!visibility private
|
91
92
|
def apply_deref_options(value) # :nodoc:
|
92
93
|
return nil if value.nil?
|
93
94
|
return value if @do_nothing_on_deref
|
94
|
-
value = value
|
95
95
|
value = @copy_on_deref.call(value) if @copy_on_deref
|
96
96
|
value = value.dup if @dup_on_deref
|
97
97
|
value = value.freeze if @freeze_on_deref
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# Raised when errors occur during configuration.
|
4
|
+
ConfigurationError = Class.new(StandardError)
|
5
|
+
|
6
|
+
# Raised when a lifecycle method (such as `stop`) is called in an improper
|
7
|
+
# sequence or when the object is in an inappropriate state.
|
8
|
+
LifecycleError = Class.new(StandardError)
|
9
|
+
|
10
|
+
# Raised when an object's methods are called when it has not been
|
11
|
+
# properly initialized.
|
12
|
+
InitializationError = Class.new(StandardError)
|
13
|
+
|
14
|
+
# Raised when an object with a start/stop lifecycle has been started an
|
15
|
+
# excessive number of times. Often used in conjunction with a restart
|
16
|
+
# policy or strategy.
|
17
|
+
MaxRestartFrequencyError = Class.new(StandardError)
|
18
|
+
|
19
|
+
# Raised when an attempt is made to modify an immutable object
|
20
|
+
# (such as an `IVar`) after its final state has been set.
|
21
|
+
MultipleAssignmentError = Class.new(StandardError)
|
22
|
+
|
23
|
+
# Raised by an `Executor` when it is unable to process a given task,
|
24
|
+
# possibly because of a reject policy or other internal error.
|
25
|
+
RejectedExecutionError = Class.new(StandardError)
|
26
|
+
|
27
|
+
# Raised when an operation times out.
|
28
|
+
TimeoutError = Class.new(StandardError)
|
29
|
+
|
30
|
+
end
|
@@ -1,12 +1,18 @@
|
|
1
|
+
require 'concurrent/errors'
|
2
|
+
require 'concurrent/logging'
|
1
3
|
require 'concurrent/atomic/event'
|
2
4
|
|
3
5
|
module Concurrent
|
4
6
|
|
5
|
-
# An exception class raised when the maximum queue size is reached and the
|
6
|
-
# `overflow_policy` is set to `:abort`.
|
7
|
-
RejectedExecutionError = Class.new(StandardError)
|
8
|
-
|
9
7
|
module Executor
|
8
|
+
def can_overflow?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module RubyExecutor
|
14
|
+
include Executor
|
15
|
+
include Logging
|
10
16
|
|
11
17
|
# Submit a task to the executor for asynchronous processing.
|
12
18
|
#
|
@@ -123,6 +129,7 @@ module Concurrent
|
|
123
129
|
if RUBY_PLATFORM == 'java'
|
124
130
|
|
125
131
|
module JavaExecutor
|
132
|
+
include Executor
|
126
133
|
|
127
134
|
# Submit a task to the executor for asynchronous processing.
|
128
135
|
#
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Concurrent
|
2
2
|
|
3
|
-
# Ensures that jobs are passed to the
|
3
|
+
# Ensures that jobs are passed to the given executors one by one,
|
4
4
|
# never running at the same time.
|
5
5
|
class OneByOne
|
6
6
|
|
@@ -30,16 +30,24 @@ module Concurrent
|
|
30
30
|
# @raise [ArgumentError] if no task is given
|
31
31
|
def post(executor, *args, &task)
|
32
32
|
return nil if task.nil?
|
33
|
+
# FIXME Agent#send-off will blow up here
|
34
|
+
# if executor.can_overflow?
|
35
|
+
# raise ArgumentError, 'OneByOne cannot be used in conjunction with executor which may overflow'
|
36
|
+
# end
|
37
|
+
|
33
38
|
job = Job.new executor, args, task
|
34
39
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
begin
|
41
|
+
@mutex.lock
|
42
|
+
post = if @being_executed
|
43
|
+
@stash << job
|
44
|
+
false
|
45
|
+
else
|
46
|
+
@being_executed = true
|
47
|
+
end
|
48
|
+
ensure
|
49
|
+
@mutex.unlock
|
50
|
+
end
|
43
51
|
|
44
52
|
call_job job if post
|
45
53
|
true
|
@@ -55,9 +63,13 @@ module Concurrent
|
|
55
63
|
def work(job)
|
56
64
|
job.call
|
57
65
|
ensure
|
58
|
-
|
59
|
-
|
60
|
-
|
66
|
+
begin
|
67
|
+
@mutex.lock
|
68
|
+
job = @stash.shift || (@being_executed = false)
|
69
|
+
ensure
|
70
|
+
@mutex.unlock
|
71
|
+
end
|
72
|
+
|
61
73
|
call_job job if job
|
62
74
|
end
|
63
75
|
|
@@ -4,7 +4,7 @@ module Concurrent
|
|
4
4
|
|
5
5
|
# @!macro single_thread_executor
|
6
6
|
class RubySingleThreadExecutor
|
7
|
-
include
|
7
|
+
include RubyExecutor
|
8
8
|
|
9
9
|
# Create a new thread pool.
|
10
10
|
#
|
@@ -64,6 +64,7 @@ module Concurrent
|
|
64
64
|
task.last.call(*task.first)
|
65
65
|
rescue => ex
|
66
66
|
# let it fail
|
67
|
+
log DEBUG, ex
|
67
68
|
end
|
68
69
|
end
|
69
70
|
stopped_event.set
|
@@ -8,7 +8,7 @@ module Concurrent
|
|
8
8
|
|
9
9
|
# @!macro thread_pool_executor
|
10
10
|
class RubyThreadPoolExecutor
|
11
|
-
include
|
11
|
+
include RubyExecutor
|
12
12
|
|
13
13
|
# Default maximum number of threads that will be created in the pool.
|
14
14
|
DEFAULT_MAX_POOL_SIZE = 2**15 # 32768
|
@@ -99,6 +99,10 @@ module Concurrent
|
|
99
99
|
@last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
|
100
100
|
end
|
101
101
|
|
102
|
+
def can_overflow?
|
103
|
+
@max_queue != 0
|
104
|
+
end
|
105
|
+
|
102
106
|
# The number of threads currently in the pool.
|
103
107
|
#
|
104
108
|
# @return [Integer] the length
|
@@ -233,8 +237,9 @@ module Concurrent
|
|
233
237
|
when :caller_runs
|
234
238
|
begin
|
235
239
|
yield(*args)
|
236
|
-
rescue
|
240
|
+
rescue => ex
|
237
241
|
# let it fail
|
242
|
+
log DEBUG, ex
|
238
243
|
end
|
239
244
|
true
|
240
245
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'concurrent/logging'
|
2
3
|
|
3
4
|
module Concurrent
|
4
5
|
|
5
6
|
# @!visibility private
|
6
7
|
class RubyThreadPoolWorker
|
8
|
+
include Logging
|
7
9
|
|
8
10
|
# @!visibility private
|
9
11
|
def initialize(queue, parent)
|
@@ -59,6 +61,7 @@ module Concurrent
|
|
59
61
|
task.last.call(*task.first)
|
60
62
|
rescue => ex
|
61
63
|
# let it fail
|
64
|
+
log DEBUG, ex
|
62
65
|
ensure
|
63
66
|
@last_activity = Time.now.to_f
|
64
67
|
@parent.on_end_task
|
@@ -11,7 +11,7 @@ module Concurrent
|
|
11
11
|
# monitors the set and schedules each task for execution at the appropriate
|
12
12
|
# time. Tasks are run on the global task pool or on the supplied executor.
|
13
13
|
class TimerSet
|
14
|
-
include
|
14
|
+
include RubyExecutor
|
15
15
|
|
16
16
|
# Create a new set of timed tasks.
|
17
17
|
#
|
data/lib/concurrent/future.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
-
require 'concurrent/obligation'
|
4
3
|
require 'concurrent/options_parser'
|
5
4
|
require 'concurrent/executor/safe_task_executor'
|
6
5
|
|
@@ -40,7 +39,6 @@ module Concurrent
|
|
40
39
|
# @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function
|
41
40
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future
|
42
41
|
class Future < IVar
|
43
|
-
include Obligation
|
44
42
|
|
45
43
|
# Create a new `Future` in the `:unscheduled` state.
|
46
44
|
#
|
data/lib/concurrent/ivar.rb
CHANGED
@@ -1,19 +1,35 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
+
require 'concurrent/errors'
|
3
4
|
require 'concurrent/obligation'
|
4
5
|
require 'concurrent/observable'
|
5
6
|
|
6
7
|
module Concurrent
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
# An `IVar` is a single-element container that is normally created empty, and
|
10
|
+
# can only be set once. The I in `IVar` stands for immutable. Reading an `IVar`
|
11
|
+
# normally blocks until it is set. It is safe to set and read an `IVar` from
|
12
|
+
# different threads.
|
13
|
+
#
|
14
|
+
# If you want to have some parallel task set the value in an `IVar`, you want
|
15
|
+
# a `Future`. If you want to create a graph of parallel tasks all executed when
|
16
|
+
# the values they depend on are ready you want `dataflow`. `IVar` is generally
|
17
|
+
# a low-level primitive.
|
18
|
+
#
|
19
|
+
# @example Create, set and get an `IVar`
|
20
|
+
# ivar = Concurrent::IVar.new
|
21
|
+
# ivar.set 14
|
22
|
+
# ivar.get #=> 14
|
23
|
+
# ivar.set 2 # would now be an error
|
10
24
|
class IVar
|
25
|
+
|
11
26
|
include Obligation
|
12
|
-
include
|
27
|
+
include Observable
|
13
28
|
|
14
|
-
|
29
|
+
# @!visibility private
|
30
|
+
NO_VALUE = Object.new # :nodoc:
|
15
31
|
|
16
|
-
# Create a new `
|
32
|
+
# Create a new `IVar` in the `:pending` state with the (optional) initial value.
|
17
33
|
#
|
18
34
|
# @param [Object] value the initial value
|
19
35
|
# @param [Hash] opts the options to create a message with
|
@@ -63,15 +79,24 @@ module Concurrent
|
|
63
79
|
observer
|
64
80
|
end
|
65
81
|
|
82
|
+
# Set the `IVar` to a value and wake or notify all threads waiting on it.
|
83
|
+
#
|
84
|
+
# @param [Object] value the value to store in the `IVar`
|
85
|
+
# @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already been set or otherwise completed
|
66
86
|
def set(value)
|
67
87
|
complete(true, value, nil)
|
68
88
|
end
|
69
89
|
|
90
|
+
# Set the `IVar` to failed due to some error and wake or notify all threads waiting on it.
|
91
|
+
#
|
92
|
+
# @param [Object] reason for the failure
|
93
|
+
# @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already been set or otherwise completed
|
70
94
|
def fail(reason = StandardError.new)
|
71
95
|
complete(false, nil, reason)
|
72
96
|
end
|
73
97
|
|
74
|
-
|
98
|
+
# @!visibility private
|
99
|
+
def complete(success, value, reason) # :nodoc:
|
75
100
|
mutex.synchronize do
|
76
101
|
raise MultipleAssignmentError.new('multiple assignment') if [:fulfilled, :rejected].include? @state
|
77
102
|
set_state(success, value, reason)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
# Include where logging is needed
|
5
|
+
module Logging
|
6
|
+
include Logger::Severity
|
7
|
+
|
8
|
+
# Logs through {Configuration#logger}, it can be overridden by setting @logger
|
9
|
+
# @param [Integer] level one of Logger::Severity constants
|
10
|
+
# @param [String] progname e.g. a path of an Actor
|
11
|
+
# @param [String, nil] message when nil block is used to generate the message
|
12
|
+
# @yields_return [String] a message
|
13
|
+
def log(level, progname, message = nil, &block)
|
14
|
+
(@logger || Concurrent.configuration.logger).call level, progname, message, &block
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/concurrent/mvar.rb
CHANGED
@@ -4,13 +4,39 @@ require 'concurrent/atomic/event'
|
|
4
4
|
|
5
5
|
module Concurrent
|
6
6
|
|
7
|
+
# An `MVar` is a single-element container that blocks on `get` if it is empty,
|
8
|
+
# and blocks on `put` if it is full. It is safe to use an `MVar` from
|
9
|
+
# multiple threads. `MVar` can be seen as a single-element blocking queue, or
|
10
|
+
# a rendezvous variable.
|
11
|
+
#
|
12
|
+
# An `MVar` is typically used to transfer objects between threads, where the
|
13
|
+
# sending thread will block if the previous message hasn't been taken yet by the
|
14
|
+
# receiving thread. It can also be used to control access to some global shared
|
15
|
+
# state, where threads `take` the value, perform some operation, and then
|
16
|
+
# `put` it back.
|
7
17
|
class MVar
|
8
18
|
|
9
19
|
include Dereferenceable
|
10
20
|
|
21
|
+
# Unique value that represents that an `MVar` was empty
|
11
22
|
EMPTY = Object.new
|
23
|
+
|
24
|
+
# Unique value that represents that an `MVar` timed out before it was able
|
25
|
+
# to produce a value.
|
12
26
|
TIMEOUT = Object.new
|
13
27
|
|
28
|
+
# Create a new `MVar`, either empty or with an initial value.
|
29
|
+
#
|
30
|
+
# @param [Hash] opts the options controlling how the future will be processed
|
31
|
+
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
|
32
|
+
# operation pool (for long-running operations), when `false` will execute the future on the
|
33
|
+
# global task pool (for short-running tasks)
|
34
|
+
# @option opts [object] :executor when provided will run all operations on
|
35
|
+
# this executor rather than the global thread pool (overrides :operation)
|
36
|
+
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
37
|
+
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
38
|
+
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
39
|
+
# returning the value returned from the proc
|
14
40
|
def initialize(value = EMPTY, opts = {})
|
15
41
|
@value = value
|
16
42
|
@mutex = Mutex.new
|
@@ -19,6 +45,10 @@ module Concurrent
|
|
19
45
|
set_deref_options(opts)
|
20
46
|
end
|
21
47
|
|
48
|
+
# Remove the value from an `MVar`, leaving it empty, and blocking if there
|
49
|
+
# isn't a value. A timeout can be set to limit the time spent blocked, in
|
50
|
+
# which case it returns `TIMEOUT` if the time is exceeded.
|
51
|
+
# @return [Object] the value that was taken, or `TIMEOUT`
|
22
52
|
def take(timeout = nil)
|
23
53
|
@mutex.synchronize do
|
24
54
|
wait_for_full(timeout)
|
@@ -35,6 +65,10 @@ module Concurrent
|
|
35
65
|
end
|
36
66
|
end
|
37
67
|
|
68
|
+
# Put a value into an `MVar`, blocking if there is already a value until
|
69
|
+
# it is empty. A timeout can be set to limit the time spent blocked, in
|
70
|
+
# which case it returns `TIMEOUT` if the time is exceeded.
|
71
|
+
# @return [Object] the value that was put, or `TIMEOUT`
|
38
72
|
def put(value, timeout = nil)
|
39
73
|
@mutex.synchronize do
|
40
74
|
wait_for_empty(timeout)
|
@@ -50,6 +84,11 @@ module Concurrent
|
|
50
84
|
end
|
51
85
|
end
|
52
86
|
|
87
|
+
# Atomically `take`, yield the value to a block for transformation, and then
|
88
|
+
# `put` the transformed value. Returns the transformed value. A timeout can
|
89
|
+
# be set to limit the time spent blocked, in which case it returns `TIMEOUT`
|
90
|
+
# if the time is exceeded.
|
91
|
+
# @return [Object] the transformed value, or `TIMEOUT`
|
53
92
|
def modify(timeout = nil)
|
54
93
|
raise ArgumentError.new('no block given') unless block_given?
|
55
94
|
|
@@ -68,6 +107,7 @@ module Concurrent
|
|
68
107
|
end
|
69
108
|
end
|
70
109
|
|
110
|
+
# Non-blocking version of `take`, that returns `EMPTY` instead of blocking.
|
71
111
|
def try_take!
|
72
112
|
@mutex.synchronize do
|
73
113
|
if unlocked_full?
|
@@ -81,6 +121,7 @@ module Concurrent
|
|
81
121
|
end
|
82
122
|
end
|
83
123
|
|
124
|
+
# Non-blocking version of `put`, that returns whether or not it was successful.
|
84
125
|
def try_put!(value)
|
85
126
|
@mutex.synchronize do
|
86
127
|
if unlocked_empty?
|
@@ -93,6 +134,7 @@ module Concurrent
|
|
93
134
|
end
|
94
135
|
end
|
95
136
|
|
137
|
+
# Non-blocking version of `put` that will overwrite an existing value.
|
96
138
|
def set!(value)
|
97
139
|
@mutex.synchronize do
|
98
140
|
old_value = @value
|
@@ -102,6 +144,7 @@ module Concurrent
|
|
102
144
|
end
|
103
145
|
end
|
104
146
|
|
147
|
+
# Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet.
|
105
148
|
def modify!
|
106
149
|
raise ArgumentError.new('no block given') unless block_given?
|
107
150
|
|
@@ -117,10 +160,12 @@ module Concurrent
|
|
117
160
|
end
|
118
161
|
end
|
119
162
|
|
163
|
+
# Returns if the `MVar` is currently empty.
|
120
164
|
def empty?
|
121
165
|
@mutex.synchronize { @value == EMPTY }
|
122
166
|
end
|
123
167
|
|
168
|
+
# Returns if the `MVar` currently contains a value.
|
124
169
|
def full?
|
125
170
|
not empty?
|
126
171
|
end
|