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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -8
  3. data/lib/concurrent.rb +2 -0
  4. data/lib/concurrent/actor/actor.rb +3 -3
  5. data/lib/concurrent/actor/postable.rb +1 -1
  6. data/lib/concurrent/actors.rb +0 -3
  7. data/lib/concurrent/actress.rb +75 -0
  8. data/lib/concurrent/actress/ad_hoc.rb +14 -0
  9. data/lib/concurrent/actress/context.rb +96 -0
  10. data/lib/concurrent/actress/core.rb +204 -0
  11. data/lib/concurrent/actress/core_delegations.rb +37 -0
  12. data/lib/concurrent/actress/doc.md +53 -0
  13. data/lib/concurrent/actress/envelope.rb +25 -0
  14. data/lib/concurrent/actress/errors.rb +14 -0
  15. data/lib/concurrent/actress/reference.rb +64 -0
  16. data/lib/concurrent/actress/type_check.rb +48 -0
  17. data/lib/concurrent/agent.rb +20 -11
  18. data/lib/concurrent/async.rb +54 -25
  19. data/lib/concurrent/atomic/atomic.rb +48 -0
  20. data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
  21. data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
  22. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
  23. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
  24. data/lib/concurrent/atomic/event.rb +11 -16
  25. data/lib/concurrent/atomics.rb +1 -0
  26. data/lib/concurrent/channel/channel.rb +4 -2
  27. data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
  28. data/lib/concurrent/configuration.rb +59 -47
  29. data/lib/concurrent/delay.rb +28 -12
  30. data/lib/concurrent/dereferenceable.rb +6 -6
  31. data/lib/concurrent/errors.rb +30 -0
  32. data/lib/concurrent/executor/executor.rb +11 -4
  33. data/lib/concurrent/executor/immediate_executor.rb +1 -0
  34. data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
  35. data/lib/concurrent/executor/one_by_one.rb +24 -12
  36. data/lib/concurrent/executor/per_thread_executor.rb +1 -0
  37. data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
  38. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
  39. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
  40. data/lib/concurrent/executor/timer_set.rb +1 -1
  41. data/lib/concurrent/future.rb +0 -2
  42. data/lib/concurrent/ivar.rb +31 -6
  43. data/lib/concurrent/logging.rb +17 -0
  44. data/lib/concurrent/mvar.rb +45 -0
  45. data/lib/concurrent/obligation.rb +61 -20
  46. data/lib/concurrent/observable.rb +7 -0
  47. data/lib/concurrent/promise.rb +1 -0
  48. data/lib/concurrent/runnable.rb +2 -2
  49. data/lib/concurrent/supervisor.rb +1 -2
  50. data/lib/concurrent/timer_task.rb +17 -13
  51. data/lib/concurrent/tvar.rb +113 -73
  52. data/lib/concurrent/utility/processor_count.rb +141 -116
  53. data/lib/concurrent/utility/timeout.rb +4 -5
  54. data/lib/concurrent/version.rb +1 -1
  55. data/spec/concurrent/actor/postable_shared.rb +1 -1
  56. data/spec/concurrent/actress_spec.rb +191 -0
  57. data/spec/concurrent/async_spec.rb +35 -3
  58. data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
  59. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
  60. data/spec/concurrent/atomic/atomic_spec.rb +133 -0
  61. data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
  62. data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
  63. data/spec/concurrent/configuration_spec.rb +5 -2
  64. data/spec/concurrent/delay_spec.rb +14 -0
  65. data/spec/concurrent/exchanger_spec.rb +4 -9
  66. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
  67. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
  68. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
  69. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
  70. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
  71. data/spec/concurrent/ivar_spec.rb +2 -2
  72. data/spec/concurrent/obligation_spec.rb +113 -24
  73. data/spec/concurrent/observable_shared.rb +4 -0
  74. data/spec/concurrent/observable_spec.rb +8 -3
  75. data/spec/concurrent/runnable_spec.rb +2 -2
  76. data/spec/concurrent/scheduled_task_spec.rb +1 -0
  77. data/spec/concurrent/supervisor_spec.rb +26 -11
  78. data/spec/concurrent/timer_task_spec.rb +36 -35
  79. data/spec/concurrent/tvar_spec.rb +1 -1
  80. data/spec/concurrent/utility/timer_spec.rb +8 -8
  81. data/spec/spec_helper.rb +8 -18
  82. data/spec/support/example_group_extensions.rb +48 -0
  83. metadata +23 -16
  84. data/lib/concurrent/actor/actor_context.rb +0 -77
  85. data/lib/concurrent/actor/actor_ref.rb +0 -67
  86. data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
  87. data/lib/concurrent_ruby_ext.bundle +0 -0
  88. data/spec/concurrent/actor/actor_context_spec.rb +0 -29
  89. data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
  90. data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
  91. data/spec/support/functions.rb +0 -25
@@ -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 = block
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
- result = apply_deref_options(@value)
77
+ apply_deref_options(@value)
78
+ ensure
77
79
  mutex.unlock
80
+ end
78
81
 
79
- result
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
- def execute_task_once
85
- if @state == :pending
86
- begin
87
- @value = @task.call
88
- @state = :fulfilled
89
- rescue => ex
90
- @reason = ex
91
- @state = :rejected
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
- result = apply_deref_options(@value)
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
- result = @value = val
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,5 +1,6 @@
1
1
  module Concurrent
2
2
  class ImmediateExecutor
3
+ include Executor
3
4
 
4
5
  def post(*args, &task)
5
6
  raise ArgumentError.new('no block given') unless block_given?
@@ -89,6 +89,10 @@ if RUBY_PLATFORM == 'java'
89
89
  set_shutdown_hook
90
90
  end
91
91
 
92
+ def can_overflow?
93
+ @max_queue != 0
94
+ end
95
+
92
96
  # The minimum number of threads that may be retained in the pool.
93
97
  #
94
98
  # @return [Integer] the min_length
@@ -1,6 +1,6 @@
1
1
  module Concurrent
2
2
 
3
- # Ensures that jobs are passed to the underlying executor one by one,
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
- @mutex.lock
36
- post = if @being_executed
37
- @stash << job
38
- false
39
- else
40
- @being_executed = true
41
- end
42
- @mutex.unlock
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
- @mutex.lock
59
- job = @stash.shift || (@being_executed = false)
60
- @mutex.unlock
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
 
@@ -1,6 +1,7 @@
1
1
  module Concurrent
2
2
 
3
3
  class PerThreadExecutor
4
+ include Executor
4
5
 
5
6
  def self.post(*args)
6
7
  raise ArgumentError.new('no block given') unless block_given?
@@ -4,7 +4,7 @@ module Concurrent
4
4
 
5
5
  # @!macro single_thread_executor
6
6
  class RubySingleThreadExecutor
7
- include Executor
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 Executor
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 Executor
14
+ include RubyExecutor
15
15
 
16
16
  # Create a new set of timed tasks.
17
17
  #
@@ -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
  #
@@ -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
- MultipleAssignmentError = Class.new(StandardError)
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 Concurrent::Observable
27
+ include Observable
13
28
 
14
- NO_VALUE = Object.new
29
+ # @!visibility private
30
+ NO_VALUE = Object.new # :nodoc:
15
31
 
16
- # Create a new `Ivar` in the `:pending` state with the (optional) initial value.
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
- def complete(success, value, reason)
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
@@ -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