concurrent-ruby 0.7.0.rc0-x86-linux → 0.7.0.rc1-x86-linux
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +13 -8
- data/lib/concurrent.rb +0 -1
- data/lib/concurrent/actress.rb +10 -6
- data/lib/concurrent/actress/core.rb +1 -1
- data/lib/concurrent/async.rb +39 -74
- data/lib/concurrent/atomic.rb +21 -1
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +1 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +22 -0
- data/lib/concurrent/atomic_reference/jruby.rb +2 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +36 -6
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +8 -7
- data/lib/concurrent/atomic_reference/rbx.rb +7 -4
- data/lib/concurrent/atomic_reference/ruby.rb +2 -0
- data/lib/concurrent/executor/executor.rb +118 -70
- data/lib/concurrent/executor/immediate_executor.rb +50 -1
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +14 -6
- data/lib/concurrent/executor/java_single_thread_executor.rb +1 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -10
- data/lib/concurrent/executor/per_thread_executor.rb +80 -4
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +1 -1
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +4 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +1 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +2 -0
- data/lib/concurrent/executor/serialized_execution.rb +23 -0
- data/lib/concurrent/version.rb +1 -1
- metadata +2 -4
- data/lib/concurrent/atomic_reference/delegated_update.rb +0 -28
- data/lib/concurrent/supervisor.rb +0 -343
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzRmMzc1NTUzYjJiZmQzMzExOTE4ODRmMDJmNzIzMzcyYjdhZGE5ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NTQ5YzYzZTQzNjc4NzEwOTk4N2I5NmY2ODk5ZTU5Njc1MjIyMmU0Yw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
N2FjYzQxNGYzZGI3OTJjN2Q0Yjc4YzZiNmYyYzdkZmUzNWEzZmI0YTJiN2Qy
|
10
|
+
N2UwNDQ4ZWI0NThkNzRiNjBlYmU5OTliMmU2MDRlMzJhMGFmOWI3MjA5YmJi
|
11
|
+
MjM5YmJhNjA3MGJkZGUzZTA1ZDQ0NWU2MTE1NmFjNjFlZjc2MGY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjYyM2MyYWMxYTIwNDA1ZDc2Y2U1MWRhZDFhOGFmNjJmYzJmNjQ0MTU4MDA2
|
14
|
+
MjI2YWU5OTIwOTM1YzI2NTEyMGNmNjY3ZmE3MjMxNDYxOTg1MjYyMWM5YWY0
|
15
|
+
OThkNmFkZWYyODFlZDMwNTY0MDFlOThhMDBmYTU5ZDA4MTlmOGI=
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Concurrent Ruby
|
2
|
-
[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.png)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Coverage Status](https://coveralls.io/repos/ruby-concurrency/concurrent-ruby/badge.png)](https://coveralls.io/r/ruby-concurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.png)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.png)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.png)](https://gemnasium.com/ruby-concurrency/concurrent-ruby)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.png)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Coverage Status](https://coveralls.io/repos/ruby-concurrency/concurrent-ruby/badge.png)](https://coveralls.io/r/ruby-concurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.png)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.png)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.png)](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [![Gitter chat](https://badges.gitter.im/ruby-concurrency.png)](https://gitter.im/ruby-concurrency)
|
3
3
|
|
4
4
|
<table>
|
5
5
|
<tr>
|
@@ -67,8 +67,6 @@ into several general groups:
|
|
67
67
|
[Promise](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Promise),
|
68
68
|
[ScheduledTask](https://github.com/ruby-concurrency/concurrent-ruby/wiki/ScheduledTask),
|
69
69
|
and [TimerTask](https://github.com/ruby-concurrency/concurrent-ruby/wiki/TimerTask)
|
70
|
-
* Erlang-inspired [Supervisor](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Supervisor) and other lifecycle classes/mixins
|
71
|
-
for managing long-running threads
|
72
70
|
* Thread-safe variables including [M-Structures](https://github.com/ruby-concurrency/concurrent-ruby/wiki/MVar-(M-Structure)),
|
73
71
|
[I-Structures](https://github.com/ruby-concurrency/concurrent-ruby/wiki/IVar-(I-Structure)),
|
74
72
|
[thread-local variables](https://github.com/ruby-concurrency/concurrent-ruby/wiki/ThreadLocalVar),
|
@@ -133,23 +131,30 @@ sleep(3) # do other stuff
|
|
133
131
|
task.value #=> 25.96
|
134
132
|
```
|
135
133
|
|
136
|
-
##
|
134
|
+
## Maintainers
|
137
135
|
|
138
136
|
* [Jerry D'Antonio](https://github.com/jdantonio)
|
139
137
|
* [Michele Della Torre](https://github.com/mighe)
|
140
138
|
* [Chris Seaton](https://github.com/chrisseaton)
|
141
139
|
* [Lucas Allan](https://github.com/lucasallan)
|
142
140
|
* [Petr Chalupa](https://github.com/pitr-ch)
|
143
|
-
|
144
|
-
|
145
|
-
|
141
|
+
|
142
|
+
### Contributors
|
143
|
+
|
146
144
|
* [Bill Dueber](https://github.com/billdueber)
|
147
145
|
* [Brian Shirai](https://github.com/brixen)
|
148
146
|
* [Chip Miller](https://github.com/chip-miller)
|
147
|
+
* [Giuseppe Capizzi](https://github.com/gcapizzi)
|
149
148
|
* [Jamie Hodge](https://github.com/jamiehodge)
|
149
|
+
* [Larry Lv](https://github.com/larrylv)
|
150
|
+
* [Maxim Chechel](https://github.com/maximchick)
|
151
|
+
* [Ravil Bayramgalin](https://github.com/brainopia)
|
152
|
+
* [René Föhring](https://github.com/rrrene)
|
153
|
+
* [Shane Wilton](https://github.com/ShaneWilton)
|
154
|
+
* [sheaney](https://github.com/sheaney)
|
150
155
|
* [Zander Hill](https://github.com/zph)
|
151
156
|
|
152
|
-
|
157
|
+
### Contributing
|
153
158
|
|
154
159
|
1. Fork it
|
155
160
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
data/lib/concurrent.rb
CHANGED
data/lib/concurrent/actress.rb
CHANGED
@@ -11,8 +11,8 @@ module Concurrent
|
|
11
11
|
# - Inspired by Akka and Erlang.
|
12
12
|
#
|
13
13
|
# Actors are sharing a thread-pool by default which makes them very cheap to create and discard.
|
14
|
-
# Thousands of actors can be created allowing to
|
15
|
-
# without
|
14
|
+
# Thousands of actors can be created, allowing you to break the program into small maintainable pieces,
|
15
|
+
# without violating the single responsibility principle.
|
16
16
|
#
|
17
17
|
# ## What is an actor model?
|
18
18
|
#
|
@@ -26,7 +26,7 @@ module Concurrent
|
|
26
26
|
# ## Why?
|
27
27
|
#
|
28
28
|
# Concurrency is hard this is one of many ways how to simplify the problem.
|
29
|
-
# It is simpler to reason about actors
|
29
|
+
# It is simpler to reason about actors than about locks (and all their possible states).
|
30
30
|
#
|
31
31
|
# ## How to use it
|
32
32
|
#
|
@@ -145,7 +145,7 @@ module Concurrent
|
|
145
145
|
Thread.current[:__current_actor__]
|
146
146
|
end
|
147
147
|
|
148
|
-
# implements
|
148
|
+
# implements the root actor
|
149
149
|
class Root
|
150
150
|
include Context
|
151
151
|
# to allow spawning of new actors, spawn needs to be called inside the parent Actor
|
@@ -158,8 +158,12 @@ module Concurrent
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
+
@root = Delay.new { Core.new(parent: nil, name: '/', class: Root).reference }
|
162
|
+
|
161
163
|
# A root actor, a default parent of all actors spawned outside an actor
|
162
|
-
|
164
|
+
def self.root
|
165
|
+
@root.value
|
166
|
+
end
|
163
167
|
|
164
168
|
# Spawns a new actor.
|
165
169
|
#
|
@@ -184,7 +188,7 @@ module Concurrent
|
|
184
188
|
if Actress.current
|
185
189
|
Core.new(spawn_optionify(*args).merge(parent: Actress.current), &block).reference
|
186
190
|
else
|
187
|
-
|
191
|
+
root.ask([:spawn, spawn_optionify(*args), block]).value
|
188
192
|
end
|
189
193
|
end
|
190
194
|
|
@@ -17,7 +17,7 @@ module Concurrent
|
|
17
17
|
# @return [String] the name of this instance, it should be uniq (not enforced right now)
|
18
18
|
# @!attribute [r] path
|
19
19
|
# @return [String] a path of this actor. It is used for easier orientation and logging.
|
20
|
-
# Path is constructed recursively with: `parent.path + self.name` up to a {Actress
|
20
|
+
# Path is constructed recursively with: `parent.path + self.name` up to a {Actress.root},
|
21
21
|
# e.g. `/an_actor/its_child`.
|
22
22
|
# (It will also probably form a supervision path (failures will be reported up to parents)
|
23
23
|
# in future versions.)
|
data/lib/concurrent/async.rb
CHANGED
@@ -3,8 +3,8 @@ require 'concurrent/configuration'
|
|
3
3
|
require 'concurrent/delay'
|
4
4
|
require 'concurrent/errors'
|
5
5
|
require 'concurrent/ivar'
|
6
|
-
require 'concurrent/
|
7
|
-
require 'concurrent/executor/
|
6
|
+
require 'concurrent/executor/immediate_executor'
|
7
|
+
require 'concurrent/executor/serialized_execution'
|
8
8
|
|
9
9
|
module Concurrent
|
10
10
|
|
@@ -124,69 +124,24 @@ module Concurrent
|
|
124
124
|
end
|
125
125
|
module_function :validate_argc
|
126
126
|
|
127
|
-
# Delegates synchronous, thread-safe method calls to the wrapped object.
|
128
|
-
#
|
129
|
-
# @!visibility private
|
130
|
-
class AwaitDelegator # :nodoc:
|
131
|
-
|
132
|
-
# Create a new delegator object wrapping the given `delegate` and
|
133
|
-
# protecting it with the given `mutex`.
|
134
|
-
#
|
135
|
-
# @param [Object] delegate the object to wrap and delegate method calls to
|
136
|
-
# @param [Mutex] mutex the mutex lock to use when delegating method calls
|
137
|
-
def initialize(delegate, mutex)
|
138
|
-
@delegate = delegate
|
139
|
-
@mutex = mutex
|
140
|
-
end
|
141
|
-
|
142
|
-
# Delegates method calls to the wrapped object. For performance,
|
143
|
-
# dynamically defines the given method on the delegator so that
|
144
|
-
# all future calls to `method` will not be directed here.
|
145
|
-
#
|
146
|
-
# @param [Symbol] method the method being called
|
147
|
-
# @param [Array] args zero or more arguments to the method
|
148
|
-
#
|
149
|
-
# @return [IVar] the result of the method call
|
150
|
-
#
|
151
|
-
# @raise [NameError] the object does not respond to `method` method
|
152
|
-
# @raise [ArgumentError] the given `args` do not match the arity of `method`
|
153
|
-
def method_missing(method, *args, &block)
|
154
|
-
super unless @delegate.respond_to?(method)
|
155
|
-
Async::validate_argc(@delegate, method, *args)
|
156
|
-
|
157
|
-
self.define_singleton_method(method) do |*args|
|
158
|
-
Async::validate_argc(@delegate, method, *args)
|
159
|
-
ivar = Concurrent::IVar.new
|
160
|
-
value, reason = nil, nil
|
161
|
-
begin
|
162
|
-
@mutex.synchronize do
|
163
|
-
value = @delegate.send(method, *args, &block)
|
164
|
-
end
|
165
|
-
rescue => reason
|
166
|
-
# caught
|
167
|
-
ensure
|
168
|
-
return ivar.complete(reason.nil?, value, reason)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
self.send(method, *args)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
127
|
# Delegates asynchronous, thread-safe method calls to the wrapped object.
|
177
128
|
#
|
178
129
|
# @!visibility private
|
179
130
|
class AsyncDelegator # :nodoc:
|
180
131
|
|
181
|
-
# Create a new delegator object wrapping the given
|
182
|
-
# protecting it with the given
|
132
|
+
# Create a new delegator object wrapping the given delegate,
|
133
|
+
# protecting it with the given serializer, and executing it on the
|
134
|
+
# given executor. Block if necessary.
|
183
135
|
#
|
184
136
|
# @param [Object] delegate the object to wrap and delegate method calls to
|
185
|
-
# @param [
|
186
|
-
|
137
|
+
# @param [Concurrent::Delay] executor a `Delay` wrapping the executor on which to execute delegated method calls
|
138
|
+
# @param [Concurrent::SerializedExecution] serializer the serializer to use when delegating method calls
|
139
|
+
# @param [Boolean] blocking will block awaiting result when `true`
|
140
|
+
def initialize(delegate, executor, serializer, blocking = false)
|
187
141
|
@delegate = delegate
|
188
142
|
@executor = executor
|
189
|
-
@
|
143
|
+
@serializer = serializer
|
144
|
+
@blocking = blocking
|
190
145
|
end
|
191
146
|
|
192
147
|
# Delegates method calls to the wrapped object. For performance,
|
@@ -206,11 +161,19 @@ module Concurrent
|
|
206
161
|
|
207
162
|
self.define_singleton_method(method) do |*args|
|
208
163
|
Async::validate_argc(@delegate, method, *args)
|
209
|
-
Concurrent::
|
210
|
-
|
211
|
-
|
164
|
+
ivar = Concurrent::IVar.new
|
165
|
+
value, reason = nil, nil
|
166
|
+
@serializer.post(@executor.value) do
|
167
|
+
begin
|
168
|
+
value = @delegate.send(method, *args, &block)
|
169
|
+
rescue => reason
|
170
|
+
# caught
|
171
|
+
ensure
|
172
|
+
ivar.complete(reason.nil?, value, reason)
|
212
173
|
end
|
213
174
|
end
|
175
|
+
ivar.value if @blocking
|
176
|
+
ivar
|
214
177
|
end
|
215
178
|
|
216
179
|
self.send(method, *args)
|
@@ -219,9 +182,9 @@ module Concurrent
|
|
219
182
|
|
220
183
|
# Causes the chained method call to be performed asynchronously on the
|
221
184
|
# global thread pool. The method called by this method will return a
|
222
|
-
#
|
185
|
+
# future object in the `:pending` state and the method call will have
|
223
186
|
# been scheduled on the global thread pool. The final disposition of the
|
224
|
-
# method call can be obtained by inspecting the returned
|
187
|
+
# method call can be obtained by inspecting the returned future.
|
225
188
|
#
|
226
189
|
# Before scheduling the method on the global thread pool a best-effort
|
227
190
|
# attempt will be made to validate that the method exists on the object
|
@@ -238,15 +201,15 @@ module Concurrent
|
|
238
201
|
# method call. Use *only* protected method calls when sharing the object
|
239
202
|
# between threads.
|
240
203
|
#
|
241
|
-
# @return [Concurrent::
|
204
|
+
# @return [Concurrent::IVar] the pending result of the asynchronous operation
|
242
205
|
#
|
243
206
|
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
|
244
207
|
# @raise [NameError] the object does not respond to `method` method
|
245
208
|
# @raise [ArgumentError] the given `args` do not match the arity of `method`
|
246
209
|
#
|
247
|
-
# @see Concurrent::
|
210
|
+
# @see Concurrent::IVar
|
248
211
|
def async
|
249
|
-
raise InitializationError.new('#init_mutex was never called') unless @
|
212
|
+
raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
|
250
213
|
@__async_delegator__.value
|
251
214
|
end
|
252
215
|
alias_method :future, :async
|
@@ -280,7 +243,7 @@ module Concurrent
|
|
280
243
|
#
|
281
244
|
# @see Concurrent::IVar
|
282
245
|
def await
|
283
|
-
raise InitializationError.new('#init_mutex was never called') unless @
|
246
|
+
raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
|
284
247
|
@__await_delegator__.value
|
285
248
|
end
|
286
249
|
alias_method :delay, :await
|
@@ -290,12 +253,12 @@ module Concurrent
|
|
290
253
|
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
|
291
254
|
# @raise [ArgumentError] executor has already been set
|
292
255
|
def executor=(executor)
|
293
|
-
raise InitializationError.new('#init_mutex was never called') unless @
|
294
|
-
@
|
256
|
+
raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
|
257
|
+
@__async_executor__.reconfigure { executor } or
|
295
258
|
raise ArgumentError.new('executor has already been set')
|
296
259
|
end
|
297
260
|
|
298
|
-
# Initialize the internal
|
261
|
+
# Initialize the internal serializer and other synchronization objects. This method
|
299
262
|
# *must* be called from the constructor of the including class or explicitly
|
300
263
|
# by the caller prior to calling any other methods. If `init_mutex` is *not*
|
301
264
|
# called explicitly the async/await/executor methods will raize a
|
@@ -308,12 +271,14 @@ module Concurrent
|
|
308
271
|
#
|
309
272
|
# @raise [Concurrent::InitializationError] when called more than once
|
310
273
|
def init_mutex
|
311
|
-
raise InitializationError.new('#init_mutex was already called') if @
|
312
|
-
|
313
|
-
|
314
|
-
@
|
315
|
-
@
|
316
|
-
|
274
|
+
raise InitializationError.new('#init_mutex was already called') if @__async_initialized__
|
275
|
+
@__async_initialized__ = true
|
276
|
+
serializer = Concurrent::SerializedExecution.new
|
277
|
+
@__async_executor__ = Delay.new{ Concurrent.configuration.global_operation_pool }
|
278
|
+
@__await_delegator__ = Delay.new{ AsyncDelegator.new(
|
279
|
+
self, Delay.new{ Concurrent::ImmediateExecutor.new }, serializer, true) }
|
280
|
+
@__async_delegator__ = Delay.new{ AsyncDelegator.new(
|
281
|
+
self, @__async_executor__, serializer, false) }
|
317
282
|
end
|
318
283
|
end
|
319
284
|
end
|
data/lib/concurrent/atomic.rb
CHANGED
@@ -16,30 +16,50 @@ end
|
|
16
16
|
|
17
17
|
if defined? Concurrent::JavaAtomic
|
18
18
|
|
19
|
+
# @!macro [attach] atomic_reference
|
20
|
+
#
|
21
|
+
# An object reference that may be updated atomically.
|
22
|
+
#
|
23
|
+
# @since 0.7.0.rc0
|
24
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
|
25
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
|
19
26
|
class Concurrent::Atomic < Concurrent::JavaAtomic
|
20
27
|
end
|
21
28
|
|
22
29
|
elsif defined? Concurrent::CAtomic
|
23
30
|
|
31
|
+
# @!macro [attach] concurrent_update_error
|
32
|
+
#
|
33
|
+
# This exception may be thrown by methods that have detected concurrent
|
34
|
+
# modification of an object when such modification is not permissible.
|
24
35
|
class Concurrent::Atomic < Concurrent::CAtomic
|
25
36
|
end
|
26
37
|
|
27
38
|
elsif defined? Concurrent::RbxAtomic
|
28
39
|
|
40
|
+
# @!macro atomic_reference
|
29
41
|
class Concurrent::Atomic < Concurrent::RbxAtomic
|
30
42
|
end
|
31
43
|
|
32
44
|
else
|
33
45
|
|
46
|
+
# @!macro atomic_reference
|
34
47
|
class Concurrent::Atomic < Concurrent::MutexAtomic
|
35
48
|
end
|
36
49
|
end
|
37
50
|
|
51
|
+
# @!macro atomic_reference
|
38
52
|
class Atomic < Concurrent::Atomic
|
39
53
|
|
54
|
+
# @!macro concurrent_update_error
|
40
55
|
ConcurrentUpdateError = Class.new(Concurrent::ConcurrentUpdateError)
|
41
56
|
|
42
|
-
|
57
|
+
# @!macro [attach] atomic_reference_method_initialize
|
58
|
+
#
|
59
|
+
# Creates a new Atomic reference with null initial value.
|
60
|
+
#
|
61
|
+
# @param [Object] value the initial value
|
62
|
+
def initialize(value)
|
43
63
|
warn "[DEPRECATED] Please use Concurrent::Atomic instead."
|
44
64
|
super
|
45
65
|
end
|
@@ -4,14 +4,36 @@ module Concurrent
|
|
4
4
|
|
5
5
|
# Define update methods that use direct paths
|
6
6
|
module AtomicDirectUpdate
|
7
|
+
|
8
|
+
# @!macro [attach] atomic_reference_method_update
|
9
|
+
#
|
7
10
|
# Pass the current value to the given block, replacing it
|
8
11
|
# with the block's result. May retry if the value changes
|
9
12
|
# during the block's execution.
|
13
|
+
#
|
14
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
15
|
+
# given (old) value
|
16
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
17
|
+
#
|
18
|
+
# @return [Object] the new value
|
10
19
|
def update
|
11
20
|
true until compare_and_set(old_value = get, new_value = yield(old_value))
|
12
21
|
new_value
|
13
22
|
end
|
14
23
|
|
24
|
+
# @!macro [attach] atomic_reference_method_try_update
|
25
|
+
#
|
26
|
+
# Pass the current value to the given block, replacing it
|
27
|
+
# with the block's result. Raise an exception if the update
|
28
|
+
# fails.
|
29
|
+
#
|
30
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
31
|
+
# given (old) value
|
32
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
33
|
+
#
|
34
|
+
# @return [Object] the new value
|
35
|
+
#
|
36
|
+
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
|
15
37
|
def try_update
|
16
38
|
old_value = get
|
17
39
|
new_value = yield old_value
|
@@ -4,26 +4,46 @@ require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
|
4
4
|
|
5
5
|
module Concurrent
|
6
6
|
|
7
|
-
#
|
8
|
-
class MutexAtomic
|
7
|
+
# @!macro atomic_reference
|
8
|
+
class MutexAtomic
|
9
9
|
include Concurrent::AtomicDirectUpdate
|
10
10
|
include Concurrent::AtomicNumericCompareAndSetWrapper
|
11
11
|
|
12
|
+
# @!macro atomic_reference_method_initialize
|
12
13
|
def initialize(value = nil)
|
13
14
|
@mutex = Mutex.new
|
14
15
|
@value = value
|
15
16
|
end
|
16
17
|
|
18
|
+
# @!macro [attach] atomic_reference_method_get
|
19
|
+
#
|
20
|
+
# Gets the current value.
|
21
|
+
#
|
22
|
+
# @return [Object] the current value
|
17
23
|
def get
|
18
24
|
@mutex.synchronize { @value }
|
19
25
|
end
|
20
|
-
|
26
|
+
alias_method :value, :get
|
21
27
|
|
28
|
+
# @!macro [attach] atomic_reference_method_set
|
29
|
+
#
|
30
|
+
# Sets to the given value.
|
31
|
+
#
|
32
|
+
# @param [Object] new_value the new value
|
33
|
+
#
|
34
|
+
# @return [Object] the new value
|
22
35
|
def set(new_value)
|
23
36
|
@mutex.synchronize { @value = new_value }
|
24
37
|
end
|
25
|
-
|
38
|
+
alias_method :value=, :set
|
26
39
|
|
40
|
+
# @!macro [attach] atomic_reference_method_get_and_set
|
41
|
+
#
|
42
|
+
# Atomically sets to the given value and returns the old value.
|
43
|
+
#
|
44
|
+
# @param [Object] new_value the new value
|
45
|
+
#
|
46
|
+
# @return [Object] the old value
|
27
47
|
def get_and_set(new_value)
|
28
48
|
@mutex.synchronize do
|
29
49
|
old_value = @value
|
@@ -31,9 +51,19 @@ module Concurrent
|
|
31
51
|
old_value
|
32
52
|
end
|
33
53
|
end
|
34
|
-
|
54
|
+
alias_method :swap, :get_and_set
|
35
55
|
|
36
|
-
|
56
|
+
# @!macro [attach] atomic_reference_method_compare_and_set
|
57
|
+
#
|
58
|
+
# Atomically sets the value to the given updated value if
|
59
|
+
# the current value == the expected value.
|
60
|
+
#
|
61
|
+
# @param [Object] old_value the expected value
|
62
|
+
# @param [Object] new_value the new value
|
63
|
+
#
|
64
|
+
# @return [Boolean] `true` if successful. A `false` return indicates
|
65
|
+
# that the actual value was not equal to the expected value.
|
66
|
+
def _compare_and_set(old_value, new_value) #:nodoc:
|
37
67
|
return false unless @mutex.try_lock
|
38
68
|
begin
|
39
69
|
return false unless @value.equal? old_value
|