concurrent-ruby 0.7.2 → 0.8.0.pre1
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 +1 -7
- data/README.md +65 -50
- data/lib/concurrent/atomic.rb +2 -1
- data/lib/concurrent/atomic/atomic_boolean.rb +1 -2
- data/lib/concurrent/atomic/atomic_fixnum.rb +1 -2
- data/lib/concurrent/atomic_reference/jruby.rb +0 -1
- data/lib/concurrent/atomic_reference/ruby.rb +20 -28
- data/lib/concurrent/atomics.rb +0 -2
- data/lib/concurrent/executor/thread_pool_executor.rb +2 -0
- data/lib/concurrent/executor/timer_set.rb +3 -15
- data/lib/concurrent/version.rb +2 -1
- data/lib/extension_helper.rb +23 -19
- metadata +10 -5
- data/lib/concurrent/atomic/semaphore.rb +0 -232
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92bfafa2b9e5e7a3b60cbf6dd6f785a8f515ae64
|
4
|
+
data.tar.gz: 14ca027a5eadd0daa3d10fac49860418bb9cb7bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fae6fa7ae3849e3d68578eb782e13f1dd2e149260c8ef9a5dc8bef72891af0cce9e82abb697cfe72819a1e1c1385936138b63b73f59dd71fba2c9b1bc47bcb8
|
7
|
+
data.tar.gz: 66264191f55ab2064cb527a32c84da91550e47681f0e67856535e72b3cb23875d308b8770ec74adfa921800f9c0ae79d14b2f7f7481e545e648dd98eda9ea36a
|
data/CHANGELOG.md
CHANGED
@@ -1,19 +1,13 @@
|
|
1
|
-
### Next Release v0.7.2 (
|
1
|
+
### Next Release v0.7.2 (TBD)
|
2
2
|
|
3
|
-
* New `Semaphore` class based on [java.util.concurrent.Semaphore](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html)
|
4
3
|
* New `Promise.all?` and `Promise.any?` class methods
|
5
4
|
* Renamed `:overflow_policy` on thread pools to `:fallback_policy`
|
6
5
|
* Thread pools still accept the `:overflow_policy` option but display a warning
|
7
6
|
* Thread pools now implement `fallback_policy` behavior when not running (rather than universally rejecting tasks)
|
8
7
|
* Fixed minor `set_deref_options` constructor bug in `Promise` class
|
9
|
-
* Fixed minor `require` bug in `ThreadLocalVar` class
|
10
|
-
* Fixed race condition bug in `TimerSet` class
|
11
|
-
* Fixed race condition bug in `TimerSet` class
|
12
|
-
* Fixed signal bug in `TimerSet#post` method
|
13
8
|
* Numerous non-functional updates to clear warning when running in debug mode
|
14
9
|
* Fixed more intermittently failing tests
|
15
10
|
* Tests now run on new Travis build environment
|
16
|
-
* Multiple documentation updates
|
17
11
|
|
18
12
|
## Current Release v0.7.1 (4 December 2014)
|
19
13
|
|
data/README.md
CHANGED
@@ -38,9 +38,7 @@
|
|
38
38
|
### Supported Ruby versions
|
39
39
|
|
40
40
|
MRI 1.9.3, 2.0, 2.1, JRuby (1.9 mode), and Rubinius 2.x are supported.
|
41
|
-
|
42
|
-
is available in pure Ruby. This gem should be fully compatible with any interpreter that is
|
43
|
-
compliant with Ruby 1.9.3 or newer.
|
41
|
+
This gem should be fully compatible with any interpreter that is compliant with Ruby 1.9.3 or newer.
|
44
42
|
|
45
43
|
## Features & Documentation
|
46
44
|
|
@@ -54,18 +52,19 @@ This library contains a variety of concurrency abstractions at high and low leve
|
|
54
52
|
|
55
53
|
### High-level, general-purpose asynchronous concurrency abstractions
|
56
54
|
|
57
|
-
* [Actor](
|
58
|
-
* [Agent](
|
59
|
-
* [Async](
|
60
|
-
* [Future](
|
61
|
-
* [Dataflow](
|
62
|
-
* [Promise](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Promise.html): Similar to Futures, with more features.
|
63
|
-
* [ScheduledTask](
|
55
|
+
* [Actor](./doc/actor/main.md): Implements the Actor Model, where concurrent actors exchange messages.
|
56
|
+
* [Agent](./doc/agent.md): A single atomic value that represents an identity.
|
57
|
+
* [Async](./doc/async.md): A mixin module that provides simple asynchronous behavior to any standard class/object or object.
|
58
|
+
* [Future](./doc/future.md): An asynchronous operation that produces a value.
|
59
|
+
* [Dataflow](./doc/dataflow.md): Built on Futures, Dataflow allows you to create a task that will be scheduled when all of its data dependencies are available.
|
60
|
+
* [Promise](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Promise.html): Similar to Futures, with more features.
|
61
|
+
* [ScheduledTask](./doc/scheduled_task.md): Like a Future scheduled for a specific future time.
|
64
62
|
* [TimerTask](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/TimerTask.html): A Thread that periodically wakes up to perform work at regular intervals.
|
63
|
+
* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Channel.html): Communicating Sequential Processes (CSP).
|
65
64
|
|
66
65
|
### Java-inspired ThreadPools and other executors
|
67
66
|
|
68
|
-
* See [ThreadPool](
|
67
|
+
* See [ThreadPool](./doc/thread_pools.md) overview, which also contains a list of other Executors available.
|
69
68
|
|
70
69
|
### Thread-safe Observers
|
71
70
|
|
@@ -74,7 +73,6 @@ This library contains a variety of concurrency abstractions at high and low leve
|
|
74
73
|
* [CopyOnWriteObserverSet](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CopyOnWriteObserverSet.html)
|
75
74
|
|
76
75
|
### Thread synchronization classes and algorithms
|
77
|
-
|
78
76
|
Lower-level abstractions mainly used as building blocks.
|
79
77
|
|
80
78
|
* [condition](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Condition.html)
|
@@ -82,12 +80,10 @@ Lower-level abstractions mainly used as building blocks.
|
|
82
80
|
* [cyclic barrier](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CyclicBarrier.html)
|
83
81
|
* [event](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Event.html)
|
84
82
|
* [exchanger](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Exchanger.html)
|
85
|
-
* [semaphore](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Semaphore.html)
|
86
83
|
* [timeout](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timeout-class_method)
|
87
84
|
* [timer](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timer-class_method)
|
88
85
|
|
89
86
|
### Thread-safe variables
|
90
|
-
|
91
87
|
Lower-level abstractions mainly used as building blocks.
|
92
88
|
|
93
89
|
* [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicBoolean.html)
|
@@ -96,30 +92,47 @@ Lower-level abstractions mainly used as building blocks.
|
|
96
92
|
* [I-Structures](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/IVar.html) (IVar)
|
97
93
|
* [M-Structures](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MVar.html) (MVar)
|
98
94
|
* [thread-local variables](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadLocalVar.html)
|
99
|
-
* [software transactional memory](
|
95
|
+
* [software transactional memory](./doc/tvar.md) (TVar)
|
100
96
|
|
101
|
-
##
|
97
|
+
## Usage
|
102
98
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
on installation. Additionally, to ensure compatability with the largest possible number
|
109
|
-
of Ruby interpreters, the C extensions will *never* load under any Ruby other than MRI,
|
110
|
-
even when installed.
|
99
|
+
All abstractions within this gem can be loaded simply by requiring it:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
require 'concurrent'
|
103
|
+
```
|
111
104
|
|
112
|
-
|
105
|
+
To reduce the amount of code loaded at runtime, subsets of this gem can be required:
|
113
106
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
107
|
+
```ruby
|
108
|
+
require 'concurrent' # everything
|
109
|
+
|
110
|
+
# groups
|
111
|
+
|
112
|
+
require 'concurrent/actor' # Concurrent::Actor and supporting code
|
113
|
+
require 'concurrent/atomics' # atomic and thread synchronization classes
|
114
|
+
require 'concurrent/channels' # Concurrent::Channel and supporting code
|
115
|
+
require 'concurrent/executors' # Thread pools and other executors
|
116
|
+
require 'concurrent/utilities' # utility methods such as processor count and timers
|
117
|
+
|
118
|
+
# individual abstractions
|
119
|
+
|
120
|
+
require 'concurrent/agent' # Concurrent::Agent
|
121
|
+
require 'concurrent/async' # Concurrent::Async
|
122
|
+
require 'concurrent/atomic' # Concurrent::Atomic (formerly the `atomic` gem)
|
123
|
+
require 'concurrent/dataflow' # Concurrent::dataflow
|
124
|
+
require 'concurrent/delay' # Concurrent::Delay
|
125
|
+
require 'concurrent/exchanger' # Concurrent::Exchanger
|
126
|
+
require 'concurrent/future' # Concurrent::Future
|
127
|
+
require 'concurrent/ivar' # Concurrent::IVar
|
128
|
+
require 'concurrent/mvar' # Concurrent::MVar
|
129
|
+
require 'concurrent/promise' # Concurrent::Promise
|
130
|
+
require 'concurrent/scheduled_task' # Concurrent::ScheduledTask
|
131
|
+
require 'concurrent/timer_task' # Concurrent::TimerTask
|
132
|
+
require 'concurrent/tvar' # Concurrent::TVar
|
133
|
+
```
|
121
134
|
|
122
|
-
|
135
|
+
## Installation
|
123
136
|
|
124
137
|
```shell
|
125
138
|
gem install concurrent-ruby
|
@@ -133,33 +146,34 @@ gem 'concurrent-ruby'
|
|
133
146
|
|
134
147
|
and run `bundle install` from your shell.
|
135
148
|
|
136
|
-
###
|
149
|
+
### C Extensions for MRI
|
137
150
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
151
|
+
Potential performance improvements may be achieved under MRI by installing optional C extensions.
|
152
|
+
To minimize installation errors the C extensions are available in the `concurrent-ruby-ext` extension
|
153
|
+
gem. The extension gem lists `concurrent-ruby` as a dependency so it is not necessary to install both.
|
154
|
+
Simply install the extension gen:
|
142
155
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
even when using the native optimizations. Please see the [documentation](http://ruby-concurrency.github.io/concurrent-ruby/)
|
147
|
-
for more details.
|
156
|
+
```shell
|
157
|
+
gem install concurrent-ruby-ext
|
158
|
+
```
|
148
159
|
|
149
|
-
|
160
|
+
or add the following line to Gemfile:
|
150
161
|
|
151
|
-
```
|
152
|
-
|
153
|
-
bundle exec rake build
|
162
|
+
```ruby
|
163
|
+
gem 'concurrent-ruby-ext'
|
154
164
|
```
|
155
165
|
|
156
|
-
|
157
|
-
|
166
|
+
and run `bundle install` from your shell.
|
167
|
+
|
168
|
+
In code it is only necessary to
|
158
169
|
|
159
170
|
```shell
|
160
|
-
|
171
|
+
require 'concurrent'
|
161
172
|
```
|
162
173
|
|
174
|
+
The `concurrent-ruby` gem will automatically detect the presence of the `concurrent-ruby-ext` gem
|
175
|
+
and load the appropriate C extensions.
|
176
|
+
|
163
177
|
## Maintainers
|
164
178
|
|
165
179
|
* [Jerry D'Antonio](https://github.com/jdantonio)
|
@@ -167,6 +181,7 @@ BUILD_PURE_RUBY='true' bundle exec rake build
|
|
167
181
|
* [Chris Seaton](https://github.com/chrisseaton)
|
168
182
|
* [Lucas Allan](https://github.com/lucasallan)
|
169
183
|
* [Petr Chalupa](https://github.com/pitr-ch)
|
184
|
+
* [Paweł Obrok](https://github.com/obrok)
|
170
185
|
|
171
186
|
### Contributing
|
172
187
|
|
data/lib/concurrent/atomic.rb
CHANGED
@@ -12,6 +12,7 @@ RUBY
|
|
12
12
|
end
|
13
13
|
#####################################################################
|
14
14
|
|
15
|
+
require_relative '../extension_helper'
|
15
16
|
require 'concurrent/atomic_reference/concurrent_update_error'
|
16
17
|
require 'concurrent/atomic_reference/mutex_atomic'
|
17
18
|
|
@@ -77,7 +78,7 @@ elsif defined? Concurrent::RbxAtomic
|
|
77
78
|
class Concurrent::Atomic < Concurrent::RbxAtomic
|
78
79
|
end
|
79
80
|
|
80
|
-
elsif Concurrent
|
81
|
+
elsif defined? Concurrent::CAtomic
|
81
82
|
|
82
83
|
# @!macro atomic_reference
|
83
84
|
class Concurrent::Atomic < Concurrent::CAtomic
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative '../../extension_helper'
|
2
|
-
Concurrent.safe_require_c_extensions
|
3
2
|
|
4
3
|
module Concurrent
|
5
4
|
|
@@ -162,7 +161,7 @@ module Concurrent
|
|
162
161
|
class AtomicBoolean < JavaAtomicBoolean
|
163
162
|
end
|
164
163
|
|
165
|
-
elsif
|
164
|
+
elsif defined?(CAtomicBoolean)
|
166
165
|
|
167
166
|
# @!macro atomic_boolean
|
168
167
|
class CAtomicBoolean
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative '../../extension_helper'
|
2
|
-
Concurrent.safe_require_c_extensions
|
3
2
|
|
4
3
|
module Concurrent
|
5
4
|
|
@@ -166,7 +165,7 @@ module Concurrent
|
|
166
165
|
class AtomicFixnum < JavaAtomicFixnum
|
167
166
|
end
|
168
167
|
|
169
|
-
elsif
|
168
|
+
elsif defined?(CAtomicFixnum)
|
170
169
|
|
171
170
|
# @!macro atomic_fixnum
|
172
171
|
class CAtomicFixnum
|
@@ -1,37 +1,29 @@
|
|
1
|
-
|
1
|
+
if defined? Concurrent::CAtomic
|
2
|
+
require_relative '../../extension_helper'
|
3
|
+
require 'concurrent/atomic_reference/direct_update'
|
4
|
+
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
2
5
|
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'concurrent_ruby_ext'
|
6
|
-
rescue LoadError
|
7
|
-
# may be a Windows cross-compiled native gem
|
8
|
-
require "#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
require 'concurrent/atomic_reference/direct_update'
|
13
|
-
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
14
|
-
|
15
|
-
module Concurrent
|
6
|
+
module Concurrent
|
16
7
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
# @!macro atomic_reference
|
9
|
+
class CAtomic
|
10
|
+
include Concurrent::AtomicDirectUpdate
|
11
|
+
include Concurrent::AtomicNumericCompareAndSetWrapper
|
21
12
|
|
22
|
-
|
23
|
-
|
13
|
+
# @!method initialize
|
14
|
+
# @!macro atomic_reference_method_initialize
|
24
15
|
|
25
|
-
|
26
|
-
|
16
|
+
# @!method get
|
17
|
+
# @!macro atomic_reference_method_get
|
27
18
|
|
28
|
-
|
29
|
-
|
19
|
+
# @!method set
|
20
|
+
# @!macro atomic_reference_method_set
|
30
21
|
|
31
|
-
|
32
|
-
|
22
|
+
# @!method get_and_set
|
23
|
+
# @!macro atomic_reference_method_get_and_set
|
33
24
|
|
34
|
-
|
35
|
-
|
25
|
+
# @!method _compare_and_set
|
26
|
+
# @!macro atomic_reference_method_compare_and_set
|
27
|
+
end
|
36
28
|
end
|
37
29
|
end
|
data/lib/concurrent/atomics.rb
CHANGED
@@ -49,6 +49,8 @@ module Concurrent
|
|
49
49
|
# * `:discard`: Discard the task and return false.
|
50
50
|
# * `:caller_runs`: Execute the task on the calling thread.
|
51
51
|
#
|
52
|
+
# {include:file:doc/thread_pools.md}
|
53
|
+
#
|
52
54
|
# @note When running on the JVM (JRuby) this class will inherit from `JavaThreadPoolExecutor`.
|
53
55
|
# On all other platforms it will inherit from `RubyThreadPoolExecutor`.
|
54
56
|
#
|
@@ -55,10 +55,10 @@ module Concurrent
|
|
55
55
|
@queue.push(Task.new(time, args, task))
|
56
56
|
@timer_executor.post(&method(:process_tasks))
|
57
57
|
end
|
58
|
+
|
59
|
+
true
|
58
60
|
end
|
59
61
|
|
60
|
-
@condition.signal
|
61
|
-
true
|
62
62
|
end
|
63
63
|
|
64
64
|
# For a timer, #kill is like an orderly shutdown, except we need to manually
|
@@ -129,20 +129,8 @@ module Concurrent
|
|
129
129
|
interval = task.time - Time.now.to_f
|
130
130
|
|
131
131
|
if interval <= 0
|
132
|
-
# We need to remove the task from the queue before passing
|
133
|
-
# it to the executor, to avoid race conditions where we pass
|
134
|
-
# the peek'ed task to the executor and then pop a different
|
135
|
-
# one that's been added in the meantime.
|
136
|
-
#
|
137
|
-
# Note that there's no race condition between the peek and
|
138
|
-
# this pop - this pop could retrieve a different task from
|
139
|
-
# the peek, but that task would be due to fire now anyway
|
140
|
-
# (because @queue is a priority queue, and this thread is
|
141
|
-
# the only reader, so whatever timer is at the head of the
|
142
|
-
# queue now must have the same pop time, or a closer one, as
|
143
|
-
# when we peeked).
|
144
|
-
task = mutex.synchronize { @queue.pop }
|
145
132
|
@task_executor.post(*task.args, &task.op)
|
133
|
+
mutex.synchronize { @queue.pop }
|
146
134
|
else
|
147
135
|
mutex.synchronize do
|
148
136
|
@condition.wait(mutex, [interval, 60].min)
|
data/lib/concurrent/version.rb
CHANGED
data/lib/extension_helper.rb
CHANGED
@@ -1,28 +1,32 @@
|
|
1
1
|
module Concurrent
|
2
|
+
|
3
|
+
@@c_ext_loaded ||= false
|
4
|
+
@@java_ext_loaded ||= false
|
2
5
|
|
3
6
|
# @!visibility private
|
4
7
|
def self.allow_c_extensions?
|
5
8
|
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
6
9
|
end
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
11
|
+
if allow_c_extensions? && !@@c_ext_loaded
|
12
|
+
begin
|
13
|
+
require 'concurrent_ruby_ext'
|
14
|
+
@@c_ext_loaded = true
|
15
|
+
rescue LoadError
|
16
|
+
# may be a Windows cross-compiled native gem
|
17
|
+
begin
|
18
|
+
require "#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
|
19
|
+
@@c_ext_loaded = true
|
20
|
+
rescue LoadError
|
21
|
+
warn 'Performance on MRI may be improved with the concurrent-ruby-ext gem. Please see http://concurrent-ruby.com'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
elsif RUBY_PLATFORM == 'java' && !@@java_ext_loaded
|
25
|
+
begin
|
26
|
+
require 'concurrent_ruby_ext'
|
27
|
+
@@java_ext_loaded = true
|
28
|
+
rescue LoadError
|
29
|
+
#warn 'Attempted to load Java extensions on unsupported platform. Continuing with pure-Ruby.'
|
30
|
+
end
|
27
31
|
end
|
28
32
|
end
|
metadata
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ref
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
- - ">="
|
18
21
|
- !ruby/object:Gem::Version
|
19
22
|
version: 1.0.5
|
20
23
|
type: :runtime
|
@@ -22,6 +25,9 @@ dependencies:
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.0'
|
30
|
+
- - ">="
|
25
31
|
- !ruby/object:Gem::Version
|
26
32
|
version: 1.0.5
|
27
33
|
description: |2
|
@@ -81,7 +87,6 @@ files:
|
|
81
87
|
- lib/concurrent/atomic/count_down_latch.rb
|
82
88
|
- lib/concurrent/atomic/cyclic_barrier.rb
|
83
89
|
- lib/concurrent/atomic/event.rb
|
84
|
-
- lib/concurrent/atomic/semaphore.rb
|
85
90
|
- lib/concurrent/atomic/synchronization.rb
|
86
91
|
- lib/concurrent/atomic/thread_local_var.rb
|
87
92
|
- lib/concurrent/atomic_reference/concurrent_update_error.rb
|
@@ -162,9 +167,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
167
|
version: 1.9.3
|
163
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
169
|
requirements:
|
165
|
-
- - "
|
170
|
+
- - ">"
|
166
171
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
172
|
+
version: 1.3.1
|
168
173
|
requirements: []
|
169
174
|
rubyforge_project:
|
170
175
|
rubygems_version: 2.4.5
|
@@ -1,232 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/condition'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
class MutexSemaphore
|
5
|
-
# @!macro [attach] semaphore_method_initialize
|
6
|
-
#
|
7
|
-
# Create a new `Semaphore` with the initial `count`.
|
8
|
-
#
|
9
|
-
# @param [Fixnum] count the initial count
|
10
|
-
#
|
11
|
-
# @raise [ArgumentError] if `count` is not an integer or is less than zero
|
12
|
-
def initialize(count)
|
13
|
-
unless count.is_a?(Fixnum) && count >= 0
|
14
|
-
fail ArgumentError, 'count must be an non-negative integer'
|
15
|
-
end
|
16
|
-
@mutex = Mutex.new
|
17
|
-
@condition = Condition.new
|
18
|
-
@free = count
|
19
|
-
end
|
20
|
-
|
21
|
-
# @!macro [attach] semaphore_method_acquire
|
22
|
-
#
|
23
|
-
# Acquires the given number of permits from this semaphore,
|
24
|
-
# blocking until all are available.
|
25
|
-
#
|
26
|
-
# @param [Fixnum] permits Number of permits to acquire
|
27
|
-
#
|
28
|
-
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
29
|
-
# one
|
30
|
-
#
|
31
|
-
# @return [Nil]
|
32
|
-
def acquire(permits = 1)
|
33
|
-
unless permits.is_a?(Fixnum) && permits > 0
|
34
|
-
fail ArgumentError, 'permits must be an integer greater than zero'
|
35
|
-
end
|
36
|
-
@mutex.synchronize do
|
37
|
-
try_acquire_timed(permits, nil)
|
38
|
-
nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# @!macro [attach] semaphore_method_available_permits
|
43
|
-
#
|
44
|
-
# Returns the current number of permits available in this semaphore.
|
45
|
-
#
|
46
|
-
# @return [Integer]
|
47
|
-
def available_permits
|
48
|
-
@mutex.synchronize { @free }
|
49
|
-
end
|
50
|
-
|
51
|
-
# @!macro [attach] semaphore_method_drain_permits
|
52
|
-
#
|
53
|
-
# Acquires and returns all permits that are immediately available.
|
54
|
-
#
|
55
|
-
# @return [Integer]
|
56
|
-
def drain_permits
|
57
|
-
@mutex.synchronize do
|
58
|
-
@free.tap { |_| @free = 0 }
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# @!macro [attach] semaphore_method_try_acquire
|
63
|
-
#
|
64
|
-
# Acquires the given number of permits from this semaphore,
|
65
|
-
# only if all are available at the time of invocation or within
|
66
|
-
# `timeout` interval
|
67
|
-
#
|
68
|
-
# @param [Fixnum] permits the number of permits to acquire
|
69
|
-
#
|
70
|
-
# @param [Fixnum] timeout the number of seconds to wait for the counter
|
71
|
-
# or `nil` to return immediately
|
72
|
-
#
|
73
|
-
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
74
|
-
# one
|
75
|
-
#
|
76
|
-
# @return [Boolean] `false` if no permits are available, `true` when
|
77
|
-
# acquired a permit
|
78
|
-
def try_acquire(permits = 1, timeout = nil)
|
79
|
-
unless permits.is_a?(Fixnum) && permits > 0
|
80
|
-
fail ArgumentError, 'permits must be an integer greater than zero'
|
81
|
-
end
|
82
|
-
@mutex.synchronize do
|
83
|
-
if timeout.nil?
|
84
|
-
try_acquire_now(permits)
|
85
|
-
else
|
86
|
-
try_acquire_timed(permits, timeout)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# @!macro [attach] semaphore_method_release
|
92
|
-
#
|
93
|
-
# Releases the given number of permits, returning them to the semaphore.
|
94
|
-
#
|
95
|
-
# @param [Fixnum] permits Number of permits to return to the semaphore.
|
96
|
-
#
|
97
|
-
# @raise [ArgumentError] if `permits` is not a number or is less than one
|
98
|
-
#
|
99
|
-
# @return [Nil]
|
100
|
-
def release(permits = 1)
|
101
|
-
unless permits.is_a?(Fixnum) && permits > 0
|
102
|
-
fail ArgumentError, 'permits must be an integer greater than zero'
|
103
|
-
end
|
104
|
-
@mutex.synchronize do
|
105
|
-
@free += permits
|
106
|
-
permits.times { @condition.signal }
|
107
|
-
end
|
108
|
-
nil
|
109
|
-
end
|
110
|
-
|
111
|
-
# @!macro [attach] semaphore_method_reduce_permits
|
112
|
-
#
|
113
|
-
# @api private
|
114
|
-
#
|
115
|
-
# Shrinks the number of available permits by the indicated reduction.
|
116
|
-
#
|
117
|
-
# @param [Fixnum] reduction Number of permits to remove.
|
118
|
-
#
|
119
|
-
# @raise [ArgumentError] if `reduction` is not an integer or is negative
|
120
|
-
#
|
121
|
-
# @raise [ArgumentError] if `@free` - `@reduction` is less than zero
|
122
|
-
#
|
123
|
-
# @return [Nil]
|
124
|
-
def reduce_permits(reduction)
|
125
|
-
unless reduction.is_a?(Fixnum) && reduction >= 0
|
126
|
-
fail ArgumentError, 'reduction must be an non-negative integer'
|
127
|
-
end
|
128
|
-
@mutex.synchronize { @free -= reduction }
|
129
|
-
nil
|
130
|
-
end
|
131
|
-
|
132
|
-
private
|
133
|
-
|
134
|
-
def try_acquire_now(permits)
|
135
|
-
if @free >= permits
|
136
|
-
@free -= permits
|
137
|
-
true
|
138
|
-
else
|
139
|
-
false
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def try_acquire_timed(permits, timeout)
|
144
|
-
remaining = Condition::Result.new(timeout)
|
145
|
-
while !try_acquire_now(permits) && remaining.can_wait?
|
146
|
-
@condition.signal
|
147
|
-
remaining = @condition.wait(@mutex, remaining.remaining_time)
|
148
|
-
end
|
149
|
-
remaining.can_wait? ? true : false
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
if RUBY_PLATFORM == 'java'
|
154
|
-
|
155
|
-
# @!macro semaphore
|
156
|
-
#
|
157
|
-
# A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each {#acquire} blocks if necessary
|
158
|
-
# until a permit is available, and then takes it. Each {#release} adds a permit,
|
159
|
-
# potentially releasing a blocking acquirer.
|
160
|
-
# However, no actual permit objects are used; the Semaphore just keeps a count of the number available and
|
161
|
-
# acts accordingly.
|
162
|
-
class JavaSemaphore
|
163
|
-
# @!macro semaphore_method_initialize
|
164
|
-
def initialize(count)
|
165
|
-
unless count.is_a?(Fixnum) && count >= 0
|
166
|
-
fail(ArgumentError,
|
167
|
-
'count must be in integer greater than or equal zero')
|
168
|
-
end
|
169
|
-
@semaphore = java.util.concurrent.Semaphore.new(count)
|
170
|
-
end
|
171
|
-
|
172
|
-
# @!macro semaphore_method_acquire
|
173
|
-
def acquire(permits = 1)
|
174
|
-
unless permits.is_a?(Fixnum) && permits > 0
|
175
|
-
fail ArgumentError, 'permits must be an integer greater than zero'
|
176
|
-
end
|
177
|
-
@semaphore.acquire(permits)
|
178
|
-
end
|
179
|
-
|
180
|
-
# @!macro semaphore_method_available_permits
|
181
|
-
def available_permits
|
182
|
-
@semaphore.availablePermits
|
183
|
-
end
|
184
|
-
|
185
|
-
# @!macro semaphore_method_drain_permits
|
186
|
-
def drain_permits
|
187
|
-
@semaphore.drainPermits
|
188
|
-
end
|
189
|
-
|
190
|
-
# @!macro semaphore_method_try_acquire
|
191
|
-
def try_acquire(permits = 1, timeout = nil)
|
192
|
-
unless permits.is_a?(Fixnum) && permits > 0
|
193
|
-
fail ArgumentError, 'permits must be an integer greater than zero'
|
194
|
-
end
|
195
|
-
if timeout.nil?
|
196
|
-
@semaphore.tryAcquire(permits)
|
197
|
-
else
|
198
|
-
@semaphore.tryAcquire(permits,
|
199
|
-
timeout,
|
200
|
-
java.util.concurrent.TimeUnit::SECONDS)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
# @!macro semaphore_method_release
|
205
|
-
def release(permits = 1)
|
206
|
-
unless permits.is_a?(Fixnum) && permits > 0
|
207
|
-
fail ArgumentError, 'permits must be an integer greater than zero'
|
208
|
-
end
|
209
|
-
@semaphore.release(permits)
|
210
|
-
true
|
211
|
-
end
|
212
|
-
|
213
|
-
# @!macro semaphore_method_reduce_permits
|
214
|
-
def reduce_permits(reduction)
|
215
|
-
unless reduction.is_a?(Fixnum) && reduction >= 0
|
216
|
-
fail ArgumentError, 'reduction must be an non-negative integer'
|
217
|
-
end
|
218
|
-
@semaphore.reducePermits(reduction)
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# @!macro semaphore
|
223
|
-
class Semaphore < JavaSemaphore
|
224
|
-
end
|
225
|
-
|
226
|
-
else
|
227
|
-
|
228
|
-
# @!macro semaphore
|
229
|
-
class Semaphore < MutexSemaphore
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|