concurrent-ruby 0.3.1.pre.1 → 0.3.1.pre.2

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.
data/README.md CHANGED
@@ -6,32 +6,35 @@ Inspired by Erlang, Clojure, Go, JavaScript, actors, and classic concurrency pat
6
6
  If you find this gem useful you should check out my [functional-ruby](https://github.com/jdantonio/functional-ruby)
7
7
  gem, too. This gem uses several of the tools in that gem.
8
8
 
9
- ## Conference Presentations
10
-
11
- I've given several conference presentations on concurrent programming with this gem.
12
- Check them out:
13
-
14
- * ["Advanced Concurrent Programming in Ruby"](http://rubyconf.org/program#jerry-dantonio)
15
- at [RubyConf 2013](http://rubyconf.org/) used [this](https://github.com/jdantonio/concurrent-ruby-presentation) version of the presentation
16
- * ["Advanced Multithreading in Ruby"](http://cascadiaruby.com/#advanced-multithreading-in-ruby)
17
- at [Cascadia Ruby 2013](http://cascadiaruby.com/) used [this](https://github.com/jdantonio/concurrent-ruby-presentation/tree/cascadia-ruby-2013) version of the presentation
18
- * I'll be giving ["Advanced Concurrent Programming in Ruby"](http://codemash.org/sessions)
19
- at [CodeMash 2014](http://codemash.org/)
20
-
21
9
  ## Introduction
22
10
 
23
- The old-school "lock and synchronize" approach to concurrency is dead. The future of concurrency
24
- is asynchronous. Send out a bunch of independent [actors](http://en.wikipedia.org/wiki/Actor_model)
25
- to do your bidding and process the results when you are ready. Although the idea of the concurrent
26
- actor originated in the early 1970's it has only recently started catching on. Although there is
27
- no one "true" actor implementation (what *exactly* is "object oriented," what *exactly* is
28
- "concurrent programming"), many modern programming languages implement variations on the actor
29
- theme. This library implements a few of the most interesting and useful of those variations.
30
-
31
- Remember, *there is no silver bullet in concurrent programming.* Concurrency is hard. Very hard.
11
+ The old-school "lock and synchronize" approach to concurrency is dead. The
12
+ future of concurrency is asynchronous. Send out a bunch of independent
13
+ [actors](http://en.wikipedia.org/wiki/Actor_model) to do your bidding and
14
+ process the results when you are ready. Many modern programming languages (like
15
+ [Erlang](http://www.erlang.org/doc/reference_manual/processes.html),
16
+ [Clojure](http://clojure.org/concurrent_programming),
17
+ [Scala](http://www.scala-lang.org/api/current/index.html#scala.actors.Actor),
18
+ [Haskell](http://www.haskell.org/haskellwiki/Applications_and_libraries/Concurrency_and_parallelism#Concurrent_Haskell),
19
+ [F#](http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx),
20
+ [C#](http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx),
21
+ [Java](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html)...)
22
+ provide asynchronous concurrency mechanisms within their standard libraries, the
23
+ runtime environment, or the language iteself. This library implements a few of
24
+ the most interesting and useful of those variations.
25
+
26
+ Remember, *there is no silver bullet in concurrent programming.* Concurrency is hard.
32
27
  These tools will help ease the burden, but at the end of the day it is essential that you
33
28
  *know what you are doing.*
34
29
 
30
+ * Decouple business logic from concurrency logic
31
+ * Test business logic separate from concurrency logic
32
+ * Keep the intersection of business logic and concurrency and small as possible
33
+ * Don't share mutable data unless absolutely necessary
34
+ * Protect shared data as much as possible (prefer [immutability](https://github.com/harukizaemon/hamster))
35
+ * Don't mix Ruby's [concurrency](http://ruby-doc.org/core-2.0.0/Thread.html)
36
+ [primitives](http://www.ruby-doc.org/core-2.0.0/Mutex.html) with asynchronous concurrency libraries
37
+
35
38
  The project is hosted on the following sites:
36
39
 
37
40
  * [RubyGems project page](https://rubygems.org/gems/concurrent-ruby)
@@ -41,17 +44,19 @@ The project is hosted on the following sites:
41
44
  * [Dependency tracking on Gemnasium](https://gemnasium.com/jdantonio/concurrent-ruby)
42
45
  * [Follow me on Twitter](https://twitter.com/jerrydantonio)
43
46
 
44
- ### Goals
45
-
46
- My history with high-performance, highly-concurrent programming goes back to my days with C/C++.
47
- I have the same scars as everyone else doing that kind of work with those languages.
48
- I'm fascinated by modern concurrency patterns like [Actors](http://en.wikipedia.org/wiki/Actor_model),
49
- [Agents](http://doc.akka.io/docs/akka/snapshot/java/agents.html), and
50
- [Promises](http://promises-aplus.github.io/promises-spec/). I'm equally fascinated by languages
51
- with strong concurrency support like [Erlang](http://www.erlang.org/doc/getting_started/conc_prog.html),
52
- [Go](http://golang.org/doc/articles/concurrency_patterns.html), and
53
- [Clojure](http://clojure.org/concurrent_programming). My goal is to implement those patterns in Ruby.
54
- Specifically:
47
+ ### Conference Presentations
48
+
49
+ I've given several conference presentations on concurrent programming with this gem.
50
+ Check them out:
51
+
52
+ * ["Advanced Concurrent Programming in Ruby"](http://rubyconf.org/program#jerry-dantonio)
53
+ at [RubyConf 2013](http://rubyconf.org/) used [this](https://github.com/jdantonio/concurrent-ruby-presentation) version of the presentation
54
+ * ["Advanced Multithreading in Ruby"](http://cascadiaruby.com/#advanced-multithreading-in-ruby)
55
+ at [Cascadia Ruby 2013](http://cascadiaruby.com/) used [this](https://github.com/jdantonio/concurrent-ruby-presentation/tree/cascadia-ruby-2013) version of the presentation
56
+ * I'll be giving ["Advanced Concurrent Programming in Ruby"](http://codemash.org/sessions)
57
+ at [CodeMash 2014](http://codemash.org/)
58
+
59
+ ## Goals
55
60
 
56
61
  * Stay true to the spirit of the languages providing inspiration
57
62
  * But implement in a way that makes sense for Ruby
@@ -1,14 +1,11 @@
1
1
  require 'observer'
2
-
3
2
  require 'concurrent/obligation'
4
- require 'concurrent/runnable'
5
3
 
6
4
  module Concurrent
7
5
 
8
6
  class ScheduledTask
9
7
  include Obligation
10
8
  include Observable
11
- include Runnable
12
9
 
13
10
  attr_reader :schedule_time
14
11
 
@@ -30,9 +27,12 @@ module Concurrent
30
27
  end
31
28
 
32
29
  @state = :pending
33
- @task = block
34
30
  @schedule_time.freeze
31
+ @task = block
35
32
  set_deref_options(opts)
33
+
34
+ @thread = Thread.new{ work }
35
+ @thread.abort_on_exception = false
36
36
  end
37
37
 
38
38
  def cancelled?
@@ -55,15 +55,16 @@ module Concurrent
55
55
  end
56
56
  end
57
57
  end
58
+ alias_method :stop, :cancel
58
59
 
59
60
  def add_observer(observer, func = :update)
60
- return false unless @state == :pending || @state == :in_progress
61
+ return false unless [:pending, :in_progress].include?(@state)
61
62
  super
62
63
  end
63
64
 
64
65
  protected
65
66
 
66
- def on_task
67
+ def work
67
68
  while (diff = @schedule_time.to_f - Time.now.to_f) > 0
68
69
  sleep( diff > 60 ? 60 : diff )
69
70
  end
@@ -1,20 +1,22 @@
1
1
  require 'thread'
2
2
  require 'observer'
3
3
 
4
+ require 'concurrent/dereferenceable'
4
5
  require 'concurrent/runnable'
5
6
  require 'concurrent/utilities'
6
7
 
7
8
  module Concurrent
8
9
 
9
10
  class TimerTask
11
+ include Dereferenceable
10
12
  include Runnable
11
13
  include Observable
12
14
 
13
15
  EXECUTION_INTERVAL = 60
14
16
  TIMEOUT_INTERVAL = 30
15
17
 
16
- attr_reader :execution_interval
17
- attr_reader :timeout_interval
18
+ attr_accessor :execution_interval
19
+ attr_accessor :timeout_interval
18
20
 
19
21
  def initialize(opts = {}, &block)
20
22
  raise ArgumentError.new('no block given') unless block_given?
@@ -22,9 +24,9 @@ module Concurrent
22
24
  @execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
23
25
  @timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
24
26
  @run_now = opts[:now] || opts[:run_now] || false
25
- @block_args = opts[:args] || opts [:arguments] || []
26
27
 
27
28
  @task = block
29
+ set_deref_options(opts)
28
30
  end
29
31
 
30
32
  def kill
@@ -65,17 +67,18 @@ module Concurrent
65
67
  end
66
68
 
67
69
  def execute_task
70
+ @value = ex = nil
68
71
  @worker = Thread.new do
69
72
  Thread.current.abort_on_exception = false
70
- Thread.current[:result] = @task.call(*@block_args)
73
+ Thread.current[:result] = @task.call(self)
71
74
  end
72
75
  raise TimeoutError if @worker.join(@timeout_interval).nil?
73
- changed
74
- notify_observers(Time.now, @worker[:result], nil)
76
+ mutex.synchronize { @value = @worker[:result] }
75
77
  rescue Exception => ex
76
- changed
77
- notify_observers(Time.now, nil, ex)
78
+ # suppress
78
79
  ensure
80
+ changed
81
+ notify_observers(Time.now, self.value, ex)
79
82
  unless @worker.nil?
80
83
  Thread.kill(@worker)
81
84
  @worker = nil
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '0.3.1.pre.1'
2
+ VERSION = '0.3.1.pre.2'
3
3
  end
@@ -1,36 +1,29 @@
1
- # To Gobbler's Knob. It's Groundhog Day.
1
+ # To Gobbler's Knob. It's Groundhog Day!
2
2
 
3
3
  A very common currency pattern is to run a thread that performs a task at regular
4
4
  intervals. The thread that peforms the task sleeps for the given interval then
5
- waked up and performs the task. Later, rinse, repeat... This pattern causes two
5
+ wakes up and performs the task. Lather, rinse, repeat... This pattern causes two
6
6
  problems. First, it is difficult to test the business logic of the task becuse the
7
- task itself is tightly couple with the threading. Second, an exception in the task
8
- can cause the entire thread to abend. In a long-running application where the task
9
- thread is intended to run for days/weeks/years a crashed task thread can pose a real
10
- problem. The `TimerTask` class alleviates both problems.
11
-
12
- When a TimerTask is launched it starts a thread for monitoring the execution interval.
13
- The TimerTask thread does not perform the task, however. Instead, the TimerTask
14
- launches the task on a separat thread. The advantage of this approach is that if
15
- the task crashes it will only kill the task thread, not the TimerTask thread. The
16
- TimerTask thread can then log the success or failure of the task. The TimerTask
17
- can even be configured with a timeout value allowing it to kill a task that runs
18
- to long and then log the error.
19
-
20
- One other advantage of the `TimerTask` class is that it forces the bsiness logic to
21
- be completely decoupled from the threading logic. The business logic can be tested
22
- separately then passed to the a TimerTask for scheduling and running.
23
-
24
- The `TimerTask` is the yin to to the
25
- [Supervisor's](https://github.com/jdantonio/concurrent-ruby/blob/master/md/supervisor.md)
26
- yang. Where the `Supervisor` is intended to manage long-running threads that operate
27
- continuously, the `TimerTask` is intended to manage fairly short operations that
28
- occur repeatedly at regular intervals.
29
-
30
- Unlike some of the others concurrency objects in the library, TimerTasks do not
31
- run on the global thread pool. In my experience the types of tasks that will benefit
32
- from the `TimerTask` class tend to also be long running. For this reason they get
33
- their own thread every time the task is executed.
7
+ task itself is tightly coupled with the concurrency logic. Second, an exception in
8
+ raised while performing the task can cause the entire thread to abend. In a
9
+ long-running application where the task thread is intended to run for days/weeks/years
10
+ a crashed task thread can pose a significant problem. `TimerTask` alleviates both problems.
11
+
12
+ When a `TimerTask` is launched it starts a thread for monitoring the execution interval.
13
+ The `TimerTask` thread does not perform the task, however. Instead, the TimerTask
14
+ launches the task on a separate thread. Should the task experience an unrecoverable
15
+ crash only the task thread will crash. This makes the `TimerTask` very fault tolerant
16
+ Additionally, the `TimerTask` thread can respond to the success or failure of the task,
17
+ performing logging or ancillary operations. `TimerTask` can also be configured with a
18
+ timeout value allowing it to kill a task that runs too long.
19
+
20
+ One other advantage of `TimerTask` is it forces the bsiness logic to be completely decoupled
21
+ from the concurrency logic. The business logic can be tested separately then passed to the
22
+ `TimerTask` for scheduling and running.
23
+
24
+ Unlike other abstraction in this library, `TimerTask` does not run on the global thread pool.
25
+ In my experience the types of tasks that will benefit from `TimerTask` tend to also be long
26
+ running. For this reason they get their own thread every time the task is executed.
34
27
 
35
28
  ## Observation
36
29
 
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'timecop'
3
3
  require_relative 'obligation_shared'
4
- require_relative 'runnable_shared'
5
4
 
6
5
  module Concurrent
7
6
 
@@ -9,32 +8,21 @@ module Concurrent
9
8
 
10
9
  context 'behavior' do
11
10
 
12
- # runnable
13
-
14
- subject { ScheduledTask.new(0.5){ nil } }
15
- it_should_behave_like :runnable
16
-
17
11
  # obligation
18
12
 
19
13
  let!(:fulfilled_value) { 10 }
20
14
  let!(:rejected_reason) { StandardError.new('mojo jojo') }
21
15
 
22
16
  let(:pending_subject) do
23
- task = ScheduledTask.new(1){ fulfilled_value }
24
- task.run!
25
- task
17
+ ScheduledTask.new(1){ fulfilled_value }
26
18
  end
27
19
 
28
20
  let(:fulfilled_subject) do
29
- task = ScheduledTask.new(0.1){ fulfilled_value }
30
- task.run
31
- task
21
+ ScheduledTask.new(0.1){ fulfilled_value }.tap(){ sleep(0.2) }
32
22
  end
33
23
 
34
24
  let(:rejected_subject) do
35
- task = ScheduledTask.new(0.1){ raise rejected_reason }
36
- task.run
37
- task
25
+ ScheduledTask.new(0.1){ raise rejected_reason }.tap(){ sleep(0.2) }
38
26
  end
39
27
 
40
28
  it_should_behave_like :obligation
@@ -84,14 +72,12 @@ module Concurrent
84
72
 
85
73
  it 'returns false if the task has already been performed' do
86
74
  task = ScheduledTask.new(0.1){ 42 }
87
- task.run!
88
75
  sleep(0.2)
89
76
  task.cancel.should be_false
90
77
  end
91
78
 
92
79
  it 'returns false if the task is already in progress' do
93
80
  task = ScheduledTask.new(0.1){ sleep(1); 42 }
94
- task.run!
95
81
  sleep(0.2)
96
82
  task.cancel.should be_false
97
83
  end
@@ -99,7 +85,6 @@ module Concurrent
99
85
  it 'cancels the task if it has not yet started' do
100
86
  @expected = true
101
87
  task = ScheduledTask.new(0.3){ @expected = false }
102
- task.run!
103
88
  sleep(0.1)
104
89
  task.cancel
105
90
  sleep(0.5)
@@ -108,44 +93,25 @@ module Concurrent
108
93
 
109
94
  it 'returns true on success' do
110
95
  task = ScheduledTask.new(0.3){ @expected = false }
111
- task.run!
112
96
  sleep(0.1)
113
97
  task.cancel.should be_true
114
98
  end
115
99
 
116
100
  it 'sets the state to :cancelled when cancelled' do
117
101
  task = ScheduledTask.new(10){ 42 }
118
- task.run!
119
102
  sleep(0.1)
120
103
  task.cancel
121
104
  task.should be_cancelled
122
105
  end
123
-
124
- it 'stops the runnable' do
125
- task = ScheduledTask.new(0.2){ 42 }
126
- task.run!
127
- sleep(0.1)
128
- task.cancel
129
- sleep(0.2)
130
- task.should_not be_running
131
- end
132
106
  end
133
107
 
134
108
  context 'execution' do
135
109
 
136
110
  it 'sets the state to :in_progress when the task is running' do
137
111
  task = ScheduledTask.new(0.1){ sleep(1); 42 }
138
- task.run!
139
112
  sleep(0.2)
140
113
  task.should be_in_progress
141
114
  end
142
-
143
- it 'stops itself on task completion' do
144
- task = ScheduledTask.new(0.1){ 42 }
145
- task.run!
146
- sleep(0.2)
147
- task.should_not be_running
148
- end
149
115
  end
150
116
 
151
117
  context 'observation' do
@@ -167,13 +133,11 @@ module Concurrent
167
133
 
168
134
  it 'returns true for an observer added while :pending' do
169
135
  task = ScheduledTask.new(1){ 42 }
170
- task.run!
171
136
  task.add_observer(observer).should be_true
172
137
  end
173
138
 
174
139
  it 'returns true for an observer added while :in_progress' do
175
140
  task = ScheduledTask.new(0.1){ sleep(1); 42 }
176
- task.run!
177
141
  sleep(0.2)
178
142
  task.add_observer(observer).should be_true
179
143
  end
@@ -185,7 +149,6 @@ module Concurrent
185
149
 
186
150
  it 'returns false for an observer added once :cancelled' do
187
151
  task = ScheduledTask.new(1){ 42 }
188
- task.run!
189
152
  sleep(0.1)
190
153
  task.cancel
191
154
  sleep(0.1)
@@ -194,14 +157,12 @@ module Concurrent
194
157
 
195
158
  it 'returns false for an observer added once :fulfilled' do
196
159
  task = ScheduledTask.new(0.1){ 42 }
197
- task.run!
198
160
  sleep(0.2)
199
161
  task.add_observer(observer).should be_false
200
162
  end
201
163
 
202
164
  it 'returns false for an observer added once :rejected' do
203
165
  task = ScheduledTask.new(0.1){ raise StandardError }
204
- task.run!
205
166
  sleep(0.2)
206
167
  task.add_observer(observer).should be_false
207
168
  end
@@ -209,7 +170,6 @@ module Concurrent
209
170
  it 'notifies all observers on fulfillment' do
210
171
  task = ScheduledTask.new(0.1){ 42 }
211
172
  task.add_observer(observer)
212
- task.run!
213
173
  sleep(0.2)
214
174
  task.value.should == 42
215
175
  task.reason.should be_nil
@@ -220,7 +180,6 @@ module Concurrent
220
180
  it 'notifies all observers on rejection' do
221
181
  task = ScheduledTask.new(0.1){ raise StandardError }
222
182
  task.add_observer(observer)
223
- task.run!
224
183
  sleep(0.2)
225
184
  task.value.should be_nil
226
185
  task.reason.should be_a(StandardError)
@@ -247,7 +206,6 @@ module Concurrent
247
206
  it 'does not notify an observer added after cancellation' do
248
207
  observer.should_not_receive(:update).with(any_args())
249
208
  task = ScheduledTask.new(0.5){ 42 }
250
- task.run!
251
209
  sleep(0.1)
252
210
  task.cancel
253
211
  sleep(0.1)
@@ -65,19 +65,6 @@ module Concurrent
65
65
  }.should raise_error
66
66
  end
67
67
 
68
- it 'passes the options to the new TimerTask' do
69
- opts = {
70
- execution_interval: 100,
71
- timeout_interval: 100,
72
- run_now: false,
73
- logger: proc{ nil },
74
- block_args: %w[one two three]
75
- }
76
- @subject = TimerTask.new(opts){ nil }
77
- TimerTask.should_receive(:new).with(opts).and_return(@subject)
78
- Concurrent::TimerTask.run!(opts)
79
- end
80
-
81
68
  it 'passes the block to the new TimerTask' do
82
69
  @expected = false
83
70
  block = proc{ @expected = true }
@@ -91,6 +78,30 @@ module Concurrent
91
78
  Thread.should_receive(:new).with(any_args()).and_return(thread)
92
79
  @subject = TimerTask.run!{ nil }
93
80
  end
81
+
82
+ specify '#execution_interval is writeable' do
83
+ @subject = TimerTask.new(execution_interval: 1) do |task|
84
+ task.execution_interval = 3
85
+ end
86
+ @subject.execution_interval.should == 1
87
+ @subject.execution_interval = 0.1
88
+ @subject.execution_interval.should == 0.1
89
+ @thread = Thread.new { @subject.run }
90
+ sleep(0.2)
91
+ @subject.execution_interval.should == 3
92
+ end
93
+
94
+ specify '#execution_interval is writeable' do
95
+ @subject = TimerTask.new(timeout_interval: 1, execution_interval: 0.1) do |task|
96
+ task.timeout_interval = 3
97
+ end
98
+ @subject.timeout_interval.should == 1
99
+ @subject.timeout_interval = 2
100
+ @subject.timeout_interval.should == 2
101
+ @thread = Thread.new { @subject.run }
102
+ sleep(0.2)
103
+ @subject.timeout_interval.should == 3
104
+ end
94
105
  end
95
106
  end
96
107
 
@@ -126,15 +137,14 @@ module Concurrent
126
137
  @expected.should be_true
127
138
  end
128
139
 
129
- it 'passes any given arguments to the execution block' do
130
- args = [1,2,3,4]
140
+ it 'passes a "self" reference to the block as the sole argument' do
131
141
  @expected = nil
132
- @subject = TimerTask.new(execution_interval: 0.5, args: args) do |*args|
133
- @expected = args
142
+ @subject = TimerTask.new(execution_interval: 1, run_now: true) do |task|
143
+ @expected = task
134
144
  end
135
145
  @thread = Thread.new { @subject.run }
136
- sleep(1)
137
- @expected.should eq args
146
+ sleep(0.2)
147
+ @expected.should eq @subject
138
148
  end
139
149
 
140
150
  it 'kills the worker thread if the timeout is reached' do
metadata CHANGED
@@ -1,32 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1.pre.1
4
+ version: 0.3.1.pre.2
5
+ prerelease: 6
5
6
  platform: ruby
6
7
  authors:
7
8
  - Jerry D'Antonio
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-11-03 00:00:00.000000000 Z
12
+ date: 2013-11-06 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0'
27
- description: |2
28
- Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
29
- Inspired by Erlang, Clojure, Go, JavaScript, actors, and classic concurrency patterns.
30
+ description: ! " Modern concurrency tools including agents, futures, promises,
31
+ thread pools, actors, supervisors, and more.\n Inspired by Erlang, Clojure, Go,
32
+ JavaScript, actors, and classic concurrency patterns.\n"
30
33
  email: jerry.dantonio@gmail.com
31
34
  executables: []
32
35
  extensions: []
@@ -95,29 +98,28 @@ files:
95
98
  homepage: http://www.concurrent-ruby.com
96
99
  licenses:
97
100
  - MIT
98
- metadata: {}
99
- post_install_message: |2
100
- future = Concurrent::Future.new{ 'Hello, world!' }
101
- puts future.value
102
- #=> Hello, world!
101
+ post_install_message: ! " future = Concurrent::Future.new{ 'Hello, world!' }\n
102
+ \ puts future.value\n #=> Hello, world!\n"
103
103
  rdoc_options: []
104
104
  require_paths:
105
105
  - lib
106
106
  required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
107
108
  requirements:
108
- - - '>='
109
+ - - ! '>='
109
110
  - !ruby/object:Gem::Version
110
111
  version: 1.9.2
111
112
  required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
112
114
  requirements:
113
- - - '>'
115
+ - - ! '>'
114
116
  - !ruby/object:Gem::Version
115
117
  version: 1.3.1
116
118
  requirements: []
117
119
  rubyforge_project:
118
- rubygems_version: 2.1.10
120
+ rubygems_version: 1.8.24
119
121
  signing_key:
120
- specification_version: 4
122
+ specification_version: 3
121
123
  summary: Modern concurrency tools including agents, futures, promises, thread pools,
122
124
  actors, and more.
123
125
  test_files:
@@ -143,3 +145,4 @@ test_files:
143
145
  - spec/concurrent/utilities_spec.rb
144
146
  - spec/spec_helper.rb
145
147
  - spec/support/functions.rb
148
+ has_rdoc:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 86c7da12ad6b46036abb100e98a1450eda80fbb1
4
- data.tar.gz: 43e1758062fd717a6e4a0c690890ef8149aa7b9e
5
- SHA512:
6
- metadata.gz: 1d46489016358e9138bf0a627617ef780d2feb8e9a0d2b1f2a4ac1aa25ff8ec94dab7c72ad5c62743adc8d6ce36b0b119d80c3aa57724685aa33788e541f2aea
7
- data.tar.gz: b482b8b50ee0217bb0ed13dcb8d3c91f58984d660f91240484d867f72e7583b87fcde09558beb1349ecff39ba88e9d9ddc44863cb91f14a74d4109cf1ebcac05