concurrent-ruby 0.7.0.rc2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +106 -61
- data/lib/concurrent/actor.rb +1 -1
- data/lib/concurrent/actor/behaviour/executes_context.rb +1 -1
- data/lib/concurrent/actor/behaviour/supervised.rb +1 -1
- data/lib/concurrent/actor/behaviour/terminates_children.rb +1 -1
- data/lib/concurrent/actor/core.rb +1 -1
- data/lib/concurrent/actor/utils.rb +10 -0
- data/lib/concurrent/actor/utils/ad_hoc.rb +21 -0
- data/lib/concurrent/actor/utils/balancer.rb +40 -0
- data/lib/concurrent/actor/utils/broadcast.rb +22 -6
- data/lib/concurrent/actor/utils/pool.rb +59 -0
- data/lib/concurrent/atomic.rb +19 -25
- data/lib/concurrent/atomic/atomic_boolean.rb +4 -1
- data/lib/concurrent/atomic/atomic_fixnum.rb +4 -1
- data/lib/concurrent/atomic_reference/jruby.rb +10 -6
- data/lib/concurrent/atomic_reference/ruby.rb +14 -10
- data/lib/concurrent/executor/serialized_execution.rb +36 -23
- data/lib/concurrent/tvar.rb +1 -1
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +25 -6
- metadata +8 -6
- data/lib/concurrent/actor/ad_hoc.rb +0 -19
- data/lib/concurrent/actor/utills.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e148008b095e4bc88878fd44eca1d56841f1a50f
|
4
|
+
data.tar.gz: 13f57a25d6a852523c4b71bab1b108636aeda3c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9acfa60a0b6a0232e42dc39ecea623208b0683593f9a1061df4368d98a0ea788c83a50bde53c6207e40fff3fe61de65c4e02581ec20b85b654ef1515e9e11cc5
|
7
|
+
data.tar.gz: 90b9defb8c3ff49c8fdfa19408d17a476f6ff54905c5370c9c7deb6ea35641880112c967c4be0a6b497351c8988c840d5dde229818f48e24868e829a0306d1e9
|
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) [![Gitter chat](https://badges.gitter.im/ruby-concurrency/concurrent-ruby.png)](https://gitter.im/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) [![License](http://img.shields.io/license/MIT.png?color=green)](http://opensource.org/licenses/MIT) [![Gitter chat](https://badges.gitter.im/ruby-concurrency/concurrent-ruby.png)](https://gitter.im/ruby-concurrency/concurrent-ruby)
|
3
3
|
|
4
4
|
<table>
|
5
5
|
<tr>
|
@@ -35,22 +35,6 @@
|
|
35
35
|
</tr>
|
36
36
|
</table>
|
37
37
|
|
38
|
-
## Install
|
39
|
-
|
40
|
-
```shell
|
41
|
-
gem install concurrent-ruby
|
42
|
-
```
|
43
|
-
|
44
|
-
or add the following line to Gemfile:
|
45
|
-
|
46
|
-
```ruby
|
47
|
-
gem 'concurrent-ruby'
|
48
|
-
```
|
49
|
-
|
50
|
-
and run `bundle install` from your shell.
|
51
|
-
|
52
|
-
_NOTE: There is an old gem from 2007 called "concurrent" that does not appear to be under active development. That isn't us. Please do not run* `gem install concurrent`*. It is not the droid you are looking for._
|
53
|
-
|
54
38
|
## Features & Documentation
|
55
39
|
|
56
40
|
Please see the [Concurrent Ruby Wiki](https://github.com/ruby-concurrency/concurrent-ruby/wiki)
|
@@ -61,21 +45,27 @@ There are many concurrency abstractions in this library. These abstractions can
|
|
61
45
|
into several general groups:
|
62
46
|
|
63
47
|
* Asynchronous concurrency abstractions including
|
64
|
-
[
|
65
|
-
[
|
66
|
-
[Future](
|
67
|
-
[Promise](
|
68
|
-
[ScheduledTask](
|
69
|
-
and [TimerTask](
|
70
|
-
*
|
71
|
-
|
72
|
-
[
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
*
|
77
|
-
|
78
|
-
|
48
|
+
[Agent](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Agent.html),
|
49
|
+
[Async](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Async.html),
|
50
|
+
[Future](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Future.html),
|
51
|
+
[Promise](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Promise.html),
|
52
|
+
[ScheduledTask](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ScheduledTask.html),
|
53
|
+
and [TimerTask](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/TimerTask.html)
|
54
|
+
* Fast, light-weight [Actor model](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Actor.html) implementation.
|
55
|
+
* Thread-safe variables including
|
56
|
+
[I-Structures](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/IVar.html),
|
57
|
+
[M-Structures](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/MVar.html),
|
58
|
+
[thread-local variables](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadLocalVar.html),
|
59
|
+
and [software transactional memory](https://github.com/ruby-concurrency/concurrent-ruby/wiki/TVar-(STM))
|
60
|
+
* Thread synchronization classes and algorithms including
|
61
|
+
[condition](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Condition.html),
|
62
|
+
[countdown latch](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/CountDownLatch.html),
|
63
|
+
[dataflow](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Dataflow),
|
64
|
+
[event](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Event.html),
|
65
|
+
[exchanger](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Exchanger.html),
|
66
|
+
and [timeout](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#timeout-class_method)
|
67
|
+
* Java-inspired [executors](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Thread%20Pools) (thread pools and more)
|
68
|
+
* [And many more](http://ruby-concurrency.github.io/concurrent-ruby/index.html)...
|
79
69
|
|
80
70
|
### Semantic Versioning
|
81
71
|
|
@@ -90,7 +80,8 @@ It should be fully compatible with any interpreter that is compliant with Ruby 1
|
|
90
80
|
### Examples
|
91
81
|
|
92
82
|
Many more code examples can be found in the documentation for each class (linked above).
|
93
|
-
|
83
|
+
|
84
|
+
Future and ScheduledTask:
|
94
85
|
|
95
86
|
```ruby
|
96
87
|
require 'concurrent'
|
@@ -112,18 +103,6 @@ sleep(1) # do other stuff
|
|
112
103
|
price.value #=> 63.65
|
113
104
|
price.state #=> :fulfilled
|
114
105
|
|
115
|
-
# Promise
|
116
|
-
prices = Concurrent::Promise.new{ puts Ticker.new.get_year_end_closing('AAPL', 2013) }.
|
117
|
-
then{ puts Ticker.new.get_year_end_closing('MSFT', 2013) }.
|
118
|
-
then{ puts Ticker.new.get_year_end_closing('GOOG', 2013) }.
|
119
|
-
then{ puts Ticker.new.get_year_end_closing('AMZN', 2013) }.execute
|
120
|
-
prices.state #=> :pending
|
121
|
-
sleep(1) # do other stuff
|
122
|
-
#=> 561.02
|
123
|
-
#=> 37.41
|
124
|
-
#=> 1120.71
|
125
|
-
#=> 398.79
|
126
|
-
|
127
106
|
# ScheduledTask
|
128
107
|
task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
|
129
108
|
task.state #=> :pending
|
@@ -131,6 +110,88 @@ sleep(3) # do other stuff
|
|
131
110
|
task.value #=> 25.96
|
132
111
|
```
|
133
112
|
|
113
|
+
Actor:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
class Counter < Concurrent::Actor::Context
|
117
|
+
# Include context of an actor which gives this class access to reference
|
118
|
+
# and other information about the actor
|
119
|
+
|
120
|
+
# use initialize as you wish
|
121
|
+
def initialize(initial_value)
|
122
|
+
@count = initial_value
|
123
|
+
end
|
124
|
+
|
125
|
+
# override on_message to define actor's behaviour
|
126
|
+
def on_message(message)
|
127
|
+
if Integer === message
|
128
|
+
@count += message
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end #
|
132
|
+
|
133
|
+
# Create new actor naming the instance 'first'.
|
134
|
+
# Return value is a reference to the actor, the actual actor is never returned.
|
135
|
+
counter = Counter.spawn(:first, 5)
|
136
|
+
|
137
|
+
# Tell a message and forget returning self.
|
138
|
+
counter.tell(1)
|
139
|
+
counter << 1
|
140
|
+
# (First counter now contains 7.)
|
141
|
+
|
142
|
+
# Send a messages asking for a result.
|
143
|
+
counter.ask(0).class
|
144
|
+
counter.ask(0).value
|
145
|
+
```
|
146
|
+
|
147
|
+
## Installing and Building
|
148
|
+
|
149
|
+
This gem includes several platform-specific optimizations. To reduce the possibility of
|
150
|
+
compilation errors, we provide pre-compiled gem packages for several platforms as well
|
151
|
+
as a pure-Ruby build. Installing the gem should be no different than installing any other
|
152
|
+
Rubygems-hosted gem.
|
153
|
+
|
154
|
+
### Installing
|
155
|
+
|
156
|
+
```shell
|
157
|
+
gem install concurrent-ruby
|
158
|
+
```
|
159
|
+
|
160
|
+
or add the following line to Gemfile:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
gem 'concurrent-ruby'
|
164
|
+
```
|
165
|
+
|
166
|
+
and run `bundle install` from your shell.
|
167
|
+
|
168
|
+
### Building
|
169
|
+
|
170
|
+
Because we provide pre-compiled gem builds, users should never need to build the gem manually.
|
171
|
+
The build process for this gem is completely automated using open source tools. All of
|
172
|
+
the automation components are available in the [ruby-concurrency/rake-compiler-dev-box](https://github.com/ruby-concurrency/rake-compiler-dev-box)
|
173
|
+
GitHub repository.
|
174
|
+
|
175
|
+
This gem will compile native C code under MRI and native Java code under JRuby. It is
|
176
|
+
also possible to build a pure-Ruby version. All builds have identical functionality.
|
177
|
+
The only difference is performance. Additionally, pure-Ruby classes are always available,
|
178
|
+
even when using the native optimizations. Please see the [documentation](http://ruby-concurrency.github.io/concurrent-ruby/)
|
179
|
+
for more details.
|
180
|
+
|
181
|
+
To build and package the gem using MRI or JRuby, install the necessary build dependencies and run:
|
182
|
+
|
183
|
+
```shell
|
184
|
+
bundle exec rake compile
|
185
|
+
bundle exec rake build
|
186
|
+
```
|
187
|
+
|
188
|
+
To build and package a pure-Ruby gem, on *any* platform and interpreter
|
189
|
+
(including MRI and JRuby), run:
|
190
|
+
|
191
|
+
```shell
|
192
|
+
BUILD_PURE_RUBY='true' bundle exec rake build
|
193
|
+
```
|
194
|
+
|
134
195
|
## Maintainers
|
135
196
|
|
136
197
|
* [Jerry D'Antonio](https://github.com/jdantonio)
|
@@ -139,22 +200,6 @@ task.value #=> 25.96
|
|
139
200
|
* [Lucas Allan](https://github.com/lucasallan)
|
140
201
|
* [Petr Chalupa](https://github.com/pitr-ch)
|
141
202
|
|
142
|
-
### Contributors
|
143
|
-
|
144
|
-
* [Bill Dueber](https://github.com/billdueber)
|
145
|
-
* [Brian Shirai](https://github.com/brixen)
|
146
|
-
* [Chip Miller](https://github.com/chip-miller)
|
147
|
-
* [Giuseppe Capizzi](https://github.com/gcapizzi)
|
148
|
-
* [Jamie Hodge](https://github.com/jamiehodge)
|
149
|
-
* [Justin Lambert](https://github.com/mastfish)
|
150
|
-
* [Larry Lv](https://github.com/larrylv)
|
151
|
-
* [Maxim Chechel](https://github.com/maximchick)
|
152
|
-
* [Ravil Bayramgalin](https://github.com/brainopia)
|
153
|
-
* [René Föhring](https://github.com/rrrene)
|
154
|
-
* [Shane Wilton](https://github.com/ShaneWilton)
|
155
|
-
* [sheaney](https://github.com/sheaney)
|
156
|
-
* [Zander Hill](https://github.com/zph)
|
157
|
-
|
158
203
|
### Contributing
|
159
204
|
|
160
205
|
1. Fork it
|
data/lib/concurrent/actor.rb
CHANGED
@@ -26,7 +26,7 @@ module Concurrent
|
|
26
26
|
|
27
27
|
require 'concurrent/actor/default_dead_letter_handler'
|
28
28
|
require 'concurrent/actor/root'
|
29
|
-
require 'concurrent/actor/
|
29
|
+
require 'concurrent/actor/utils'
|
30
30
|
|
31
31
|
# @return [Reference, nil] current executing actor if any
|
32
32
|
def self.current
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Concurrent
|
2
2
|
module Actor
|
3
3
|
module Behaviour
|
4
|
-
# Delegates messages
|
4
|
+
# Delegates messages and events to {AbstractContext} instance
|
5
5
|
class ExecutesContext < Abstract
|
6
6
|
def on_envelope(envelope)
|
7
7
|
context.on_envelope envelope
|
@@ -2,7 +2,7 @@ module Concurrent
|
|
2
2
|
module Actor
|
3
3
|
module Behaviour
|
4
4
|
|
5
|
-
# Sets
|
5
|
+
# Sets and holds the supervisor of the actor if any. There is at most one supervisor
|
6
6
|
# for each actor. Each supervisor is automatically linked.
|
7
7
|
class Supervised < Abstract
|
8
8
|
attr_reader :supervisor
|
@@ -4,7 +4,7 @@ module Concurrent
|
|
4
4
|
# Terminates all children when the actor terminates.
|
5
5
|
class TerminatesChildren < Abstract
|
6
6
|
def on_event(event)
|
7
|
-
children.
|
7
|
+
children.map { |ch| ch.ask :terminate! }.each(&:wait) if event == :terminated
|
8
8
|
super event
|
9
9
|
end
|
10
10
|
end
|
@@ -37,7 +37,7 @@ module Concurrent
|
|
37
37
|
# @option opts [true, false] link, atomically link the actor to its parent
|
38
38
|
# @option opts [true, false] supervise, atomically supervise the actor by its parent
|
39
39
|
# @option opts [Array<Array(Behavior::Abstract, Array<Object>)>] behaviour_definition, array of pairs
|
40
|
-
# where each pair is behaviour class and its args, see {Behaviour.
|
40
|
+
# where each pair is behaviour class and its args, see {Behaviour.basic_behaviour_definition}
|
41
41
|
# @option opts [IVar, nil] initialized, if present it'll be set or failed after {Context} initialization
|
42
42
|
# @option opts [Proc, nil] logger a proc accepting (level, progname, message = nil, &block) params,
|
43
43
|
# can be used to hook actor instance to any logging system
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
module Utils
|
4
|
+
# Allows quick creation of actors with behaviour defined by blocks.
|
5
|
+
# @example ping
|
6
|
+
# AdHoc.spawn :forward, an_actor do |where|
|
7
|
+
# # this block has to return proc defining #on_message behaviour
|
8
|
+
# -> message { where.tell message }
|
9
|
+
# end
|
10
|
+
class AdHoc < Context
|
11
|
+
def initialize(*args, &initializer)
|
12
|
+
@on_message = Type! initializer.call(*args), Proc
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_message(message)
|
16
|
+
instance_exec message, &@on_message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
# Distributes messages between subscribed actors. Each actor'll get only one message then
|
6
|
+
# it's unsubscribed. The actor needs to resubscribe when it's ready to receive next message.
|
7
|
+
# @see Pool
|
8
|
+
class Balancer < RestartingContext
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@receivers = []
|
12
|
+
@buffer = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_message(message)
|
16
|
+
case message
|
17
|
+
when :subscribe
|
18
|
+
@receivers << envelope.sender
|
19
|
+
distribute
|
20
|
+
true
|
21
|
+
when :unsubscribe
|
22
|
+
@receivers.delete envelope.sender
|
23
|
+
true
|
24
|
+
when :subscribed?
|
25
|
+
@receivers.include? envelope.sender
|
26
|
+
else
|
27
|
+
@buffer << message
|
28
|
+
distribute
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def distribute
|
33
|
+
while !@receivers.empty? && !@buffer.empty?
|
34
|
+
@receivers.shift << @buffer.shift
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -4,8 +4,20 @@ module Concurrent
|
|
4
4
|
module Actor
|
5
5
|
module Utils
|
6
6
|
|
7
|
-
#
|
8
|
-
|
7
|
+
# Allows to build pub/sub easily.
|
8
|
+
# @example news
|
9
|
+
# news_channel = Concurrent::Actor::Utils::Broadcast.spawn :news
|
10
|
+
#
|
11
|
+
# 2.times do |i|
|
12
|
+
# Concurrent::Actor::Utils::AdHoc.spawn "listener-#{i}" do
|
13
|
+
# news_channel << :subscribe
|
14
|
+
# -> message { puts message }
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# news_channel << 'Ruby rocks!'
|
19
|
+
# # prints: 'Ruby rocks!' twice
|
20
|
+
class Broadcast < RestartingContext
|
9
21
|
|
10
22
|
def initialize
|
11
23
|
@receivers = Set.new
|
@@ -14,11 +26,14 @@ module Concurrent
|
|
14
26
|
def on_message(message)
|
15
27
|
case message
|
16
28
|
when :subscribe
|
17
|
-
|
18
|
-
|
29
|
+
if envelope.sender.is_a? Reference
|
30
|
+
@receivers.add envelope.sender
|
31
|
+
true
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
19
35
|
when :unsubscribe
|
20
|
-
|
21
|
-
true
|
36
|
+
!!@receivers.delete(envelope.sender)
|
22
37
|
when :subscribed?
|
23
38
|
@receivers.include? envelope.sender
|
24
39
|
else
|
@@ -31,6 +46,7 @@ module Concurrent
|
|
31
46
|
@receivers
|
32
47
|
end
|
33
48
|
end
|
49
|
+
|
34
50
|
end
|
35
51
|
end
|
36
52
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'concurrent/actor/utils/balancer'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Actor
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
# Allows to create a pool of workers and distribute work between them
|
8
|
+
# @param [Integer] size number of workers
|
9
|
+
# @yield [balancer, index] a block spawning an worker instance. called +size+ times.
|
10
|
+
# The worker should be descendant of AbstractWorker and supervised, see example.
|
11
|
+
# @yieldparam [Balancer] balancer to pass to the worker
|
12
|
+
# @yieldparam [Integer] index of the worker, usually used in its name
|
13
|
+
# @yieldreturn [Reference] the reference of newly created worker
|
14
|
+
# @example
|
15
|
+
# class Worker < Concurrent::Actor::Utils::AbstractWorker
|
16
|
+
# def work(message)
|
17
|
+
# p message * 5
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |balancer, index|
|
22
|
+
# Worker.spawn name: "worker-#{index}", supervise: true, args: [balancer]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# pool << 'asd' << 2
|
26
|
+
# # prints:
|
27
|
+
# # "asdasdasdasdasd"
|
28
|
+
# # 10
|
29
|
+
class Pool < RestartingContext
|
30
|
+
def initialize(size, &worker_initializer)
|
31
|
+
@balancer = Balancer.spawn name: :balancer, supervise: true
|
32
|
+
@workers = Array.new(size, &worker_initializer.curry[@balancer])
|
33
|
+
@workers.each { |w| Type! w, Reference }
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_message(message)
|
37
|
+
@balancer << message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class AbstractWorker < RestartingContext
|
42
|
+
def initialize(balancer)
|
43
|
+
@balancer = balancer
|
44
|
+
@balancer << :subscribe
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_message(message)
|
48
|
+
work message
|
49
|
+
ensure
|
50
|
+
@balancer << :subscribe
|
51
|
+
end
|
52
|
+
|
53
|
+
def work(message)
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/concurrent/atomic.rb
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
#####################################################################
|
2
|
+
# Attempt to check for the deprecated ruby-atomic gem and warn the
|
3
|
+
# user that they should use the new implementation instead.
|
4
|
+
|
5
|
+
if defined?(Atomic)
|
6
|
+
warn <<-RUBY
|
7
|
+
[ATOMIC] Detected an `Atomic` class, which may indicate a dependency
|
8
|
+
on the ruby-atomic gem. That gem has been deprecated and merged into
|
9
|
+
the concurrent-ruby gem. Please use the Concurrent::Atomic class for
|
10
|
+
atomic references and not the Atomic class.
|
11
|
+
RUBY
|
12
|
+
end
|
13
|
+
#####################################################################
|
14
|
+
|
1
15
|
require 'concurrent/atomic_reference/concurrent_update_error'
|
2
16
|
require 'concurrent/atomic_reference/mutex_atomic'
|
3
17
|
|
@@ -57,41 +71,21 @@ if defined? Concurrent::JavaAtomic
|
|
57
71
|
class Concurrent::Atomic < Concurrent::JavaAtomic
|
58
72
|
end
|
59
73
|
|
60
|
-
elsif defined? Concurrent::CAtomic
|
61
|
-
|
62
|
-
# @!macro [attach] concurrent_update_error
|
63
|
-
#
|
64
|
-
# This exception may be thrown by methods that have detected concurrent
|
65
|
-
# modification of an object when such modification is not permissible.
|
66
|
-
class Concurrent::Atomic < Concurrent::CAtomic
|
67
|
-
end
|
68
|
-
|
69
74
|
elsif defined? Concurrent::RbxAtomic
|
70
75
|
|
71
76
|
# @!macro atomic_reference
|
72
77
|
class Concurrent::Atomic < Concurrent::RbxAtomic
|
73
78
|
end
|
74
79
|
|
75
|
-
|
80
|
+
elsif Concurrent.allow_c_native_class?('CAtomic')
|
76
81
|
|
77
82
|
# @!macro atomic_reference
|
78
|
-
class Concurrent::Atomic < Concurrent::
|
83
|
+
class Concurrent::Atomic < Concurrent::CAtomic
|
79
84
|
end
|
80
|
-
end
|
81
85
|
|
82
|
-
|
83
|
-
class Atomic < Concurrent::Atomic
|
84
|
-
|
85
|
-
# @!macro concurrent_update_error
|
86
|
-
ConcurrentUpdateError = Class.new(Concurrent::ConcurrentUpdateError)
|
86
|
+
else
|
87
87
|
|
88
|
-
# @!macro
|
89
|
-
|
90
|
-
# Creates a new Atomic reference with null initial value.
|
91
|
-
#
|
92
|
-
# @param [Object] value the initial value
|
93
|
-
def initialize(value)
|
94
|
-
warn "[DEPRECATED] Please use Concurrent::Atomic instead."
|
95
|
-
super
|
88
|
+
# @!macro atomic_reference
|
89
|
+
class Concurrent::Atomic < Concurrent::MutexAtomic
|
96
90
|
end
|
97
91
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require_relative '../../extension_helper'
|
2
|
+
Concurrent.safe_require_c_extensions
|
3
|
+
|
1
4
|
module Concurrent
|
2
5
|
|
3
6
|
# @!macro [attach] atomic_boolean
|
@@ -159,7 +162,7 @@ module Concurrent
|
|
159
162
|
class AtomicBoolean < JavaAtomicBoolean
|
160
163
|
end
|
161
164
|
|
162
|
-
elsif
|
165
|
+
elsif Concurrent.allow_c_native_class?('CAtomicBoolean')
|
163
166
|
|
164
167
|
# @!macro atomic_boolean
|
165
168
|
class CAtomicBoolean
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require_relative '../../extension_helper'
|
2
|
+
Concurrent.safe_require_c_extensions
|
3
|
+
|
1
4
|
module Concurrent
|
2
5
|
|
3
6
|
# @!macro [attach] atomic_fixnum
|
@@ -163,7 +166,7 @@ module Concurrent
|
|
163
166
|
class AtomicFixnum < JavaAtomicFixnum
|
164
167
|
end
|
165
168
|
|
166
|
-
elsif
|
169
|
+
elsif Concurrent.allow_c_native_class?('CAtomicFixnum')
|
167
170
|
|
168
171
|
# @!macro atomic_fixnum
|
169
172
|
class CAtomicFixnum
|
@@ -1,10 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative '../../extension_helper'
|
2
|
+
Concurrent.safe_require_java_extensions
|
3
3
|
|
4
|
-
|
4
|
+
if defined?(Concurrent::JavaAtomic)
|
5
|
+
require 'concurrent/atomic_reference/direct_update'
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
module Concurrent
|
8
|
+
|
9
|
+
# @!macro atomic_reference
|
10
|
+
class JavaAtomic
|
11
|
+
include Concurrent::AtomicDirectUpdate
|
12
|
+
end
|
9
13
|
end
|
10
14
|
end
|
@@ -1,8 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require_relative '../../extension_helper'
|
2
|
+
|
3
|
+
if Concurrent.allow_c_extensions?
|
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
|
6
10
|
end
|
7
11
|
|
8
12
|
require 'concurrent/atomic_reference/direct_update'
|
@@ -14,19 +18,19 @@ module Concurrent
|
|
14
18
|
class CAtomic
|
15
19
|
include Concurrent::AtomicDirectUpdate
|
16
20
|
include Concurrent::AtomicNumericCompareAndSetWrapper
|
17
|
-
|
21
|
+
|
18
22
|
# @!method initialize
|
19
23
|
# @!macro atomic_reference_method_initialize
|
20
|
-
|
24
|
+
|
21
25
|
# @!method get
|
22
26
|
# @!macro atomic_reference_method_get
|
23
|
-
|
27
|
+
|
24
28
|
# @!method set
|
25
29
|
# @!macro atomic_reference_method_set
|
26
|
-
|
30
|
+
|
27
31
|
# @!method get_and_set
|
28
32
|
# @!macro atomic_reference_method_get_and_set
|
29
|
-
|
33
|
+
|
30
34
|
# @!method _compare_and_set
|
31
35
|
# @!macro atomic_reference_method_compare_and_set
|
32
36
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
require 'concurrent/executor/executor'
|
3
3
|
require 'concurrent/logging'
|
4
|
+
require 'concurrent/atomic/synchronization'
|
4
5
|
|
5
6
|
module Concurrent
|
6
7
|
|
7
8
|
# Ensures passed jobs in a serialized order never running at the same time.
|
8
9
|
class SerializedExecution
|
9
10
|
include Logging
|
11
|
+
include Synchronization
|
10
12
|
|
11
13
|
Job = Struct.new(:executor, :args, :block) do
|
12
14
|
def call
|
@@ -15,9 +17,10 @@ module Concurrent
|
|
15
17
|
end
|
16
18
|
|
17
19
|
def initialize
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
synchronize do
|
21
|
+
@being_executed = false
|
22
|
+
@stash = []
|
23
|
+
end
|
21
24
|
end
|
22
25
|
|
23
26
|
# Submit a task to the executor for asynchronous processing.
|
@@ -33,23 +36,36 @@ module Concurrent
|
|
33
36
|
#
|
34
37
|
# @raise [ArgumentError] if no task is given
|
35
38
|
def post(executor, *args, &task)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
39
|
+
posts [[executor, args, task]]
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
# As {#post} but allows to submit multiple tasks at once, it's guaranteed that they will not
|
44
|
+
# be interleaved by other tasks.
|
45
|
+
#
|
46
|
+
# @param [Array<Array(Executor, Array<Object>, Proc)>] posts array of triplets where
|
47
|
+
# first is a {Executor}, second is array of args for task, third is a task (Proc)
|
48
|
+
def posts(posts)
|
49
|
+
# if can_overflow?
|
50
|
+
# raise ArgumentError, 'SerializedExecution does not support thread-pools which can overflow'
|
51
|
+
# end
|
52
|
+
|
53
|
+
return nil if posts.empty?
|
54
|
+
|
55
|
+
jobs = posts.map { |executor, args, task| Job.new executor, args, task }
|
56
|
+
|
57
|
+
job_to_post = synchronize do
|
58
|
+
if @being_executed
|
59
|
+
@stash.push(*jobs)
|
60
|
+
nil
|
61
|
+
else
|
62
|
+
@being_executed = true
|
63
|
+
@stash.push(*jobs[1..-1])
|
64
|
+
jobs.first
|
65
|
+
end
|
50
66
|
end
|
51
67
|
|
52
|
-
call_job
|
68
|
+
call_job job_to_post if job_to_post
|
53
69
|
true
|
54
70
|
end
|
55
71
|
|
@@ -78,11 +94,8 @@ module Concurrent
|
|
78
94
|
def work(job)
|
79
95
|
job.call
|
80
96
|
ensure
|
81
|
-
|
82
|
-
@mutex.lock
|
97
|
+
synchronize do
|
83
98
|
job = @stash.shift || (@being_executed = false)
|
84
|
-
ensure
|
85
|
-
@mutex.unlock
|
86
99
|
end
|
87
100
|
|
88
101
|
call_job job if job
|
@@ -98,7 +111,7 @@ module Concurrent
|
|
98
111
|
include SerialExecutor
|
99
112
|
|
100
113
|
def initialize(executor)
|
101
|
-
@executor
|
114
|
+
@executor = executor
|
102
115
|
@serializer = SerializedExecution.new
|
103
116
|
super(executor)
|
104
117
|
end
|
data/lib/concurrent/tvar.rb
CHANGED
data/lib/concurrent/version.rb
CHANGED
data/lib/concurrent_ruby_ext.so
CHANGED
Binary file
|
data/lib/extension_helper.rb
CHANGED
@@ -1,9 +1,28 @@
|
|
1
|
-
require 'rbconfig'
|
2
|
-
|
3
1
|
module Concurrent
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
(
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
def self.allow_c_extensions?
|
5
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
6
|
+
end
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
def self.allow_c_native_class?(clazz)
|
10
|
+
allow_c_extensions? && Concurrent.const_defined?(clazz)
|
11
|
+
rescue
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
def self.safe_require_c_extensions
|
17
|
+
require 'concurrent_ruby_ext' if allow_c_extensions?
|
18
|
+
rescue LoadError
|
19
|
+
#warn 'Attempted to load C extensions on unsupported platform. Continuing with pure-Ruby.'
|
20
|
+
end
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
def self.safe_require_java_extensions
|
24
|
+
require 'concurrent_ruby_ext' if RUBY_PLATFORM == 'java'
|
25
|
+
rescue LoadError
|
26
|
+
#warn 'Attempted to load Java extensions on unsupported platform. Continuing with pure-Ruby.'
|
8
27
|
end
|
9
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0
|
4
|
+
version: 0.7.0
|
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: 2014-
|
11
|
+
date: 2014-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
|
@@ -24,7 +24,6 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- lib/concurrent.rb
|
26
26
|
- lib/concurrent/actor.rb
|
27
|
-
- lib/concurrent/actor/ad_hoc.rb
|
28
27
|
- lib/concurrent/actor/behaviour.rb
|
29
28
|
- lib/concurrent/actor/behaviour/abstract.rb
|
30
29
|
- lib/concurrent/actor/behaviour/awaits.rb
|
@@ -49,8 +48,11 @@ files:
|
|
49
48
|
- lib/concurrent/actor/reference.rb
|
50
49
|
- lib/concurrent/actor/root.rb
|
51
50
|
- lib/concurrent/actor/type_check.rb
|
52
|
-
- lib/concurrent/actor/
|
51
|
+
- lib/concurrent/actor/utils.rb
|
52
|
+
- lib/concurrent/actor/utils/ad_hoc.rb
|
53
|
+
- lib/concurrent/actor/utils/balancer.rb
|
53
54
|
- lib/concurrent/actor/utils/broadcast.rb
|
55
|
+
- lib/concurrent/actor/utils/pool.rb
|
54
56
|
- lib/concurrent/actress.rb
|
55
57
|
- lib/concurrent/agent.rb
|
56
58
|
- lib/concurrent/async.rb
|
@@ -143,9 +145,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
145
|
version: 1.9.3
|
144
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
147
|
requirements:
|
146
|
-
- - "
|
148
|
+
- - ">="
|
147
149
|
- !ruby/object:Gem::Version
|
148
|
-
version:
|
150
|
+
version: '0'
|
149
151
|
requirements: []
|
150
152
|
rubyforge_project:
|
151
153
|
rubygems_version: 2.2.2
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
module Actor
|
3
|
-
# Allows quick creation of actors with behaviour defined by blocks.
|
4
|
-
# @example ping
|
5
|
-
# AdHoc.spawn :forward, an_actor do |where|
|
6
|
-
# # this block has to return proc defining #on_message behaviour
|
7
|
-
# -> message { where.tell message }
|
8
|
-
# end
|
9
|
-
class AdHoc < Context
|
10
|
-
def initialize(*args, &initializer)
|
11
|
-
@on_message = Type! initializer.call(*args), Proc
|
12
|
-
end
|
13
|
-
|
14
|
-
def on_message(message)
|
15
|
-
instance_exec message, &@on_message
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|