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 +37 -32
- data/lib/concurrent/scheduled_task.rb +7 -6
- data/lib/concurrent/timer_task.rb +11 -8
- data/lib/concurrent/version.rb +1 -1
- data/md/timer_task.md +22 -29
- data/spec/concurrent/scheduled_task_spec.rb +3 -45
- data/spec/concurrent/timer_task_spec.rb +29 -19
- metadata +19 -16
- checksums.yaml +0 -7
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
|
24
|
-
is asynchronous. Send out a bunch of independent
|
25
|
-
to do your bidding and
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
###
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
[
|
50
|
-
[
|
51
|
-
|
52
|
-
[
|
53
|
-
|
54
|
-
|
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
|
61
|
+
return false unless [:pending, :in_progress].include?(@state)
|
61
62
|
super
|
62
63
|
end
|
63
64
|
|
64
65
|
protected
|
65
66
|
|
66
|
-
def
|
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
|
-
|
17
|
-
|
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(
|
73
|
+
Thread.current[:result] = @task.call(self)
|
71
74
|
end
|
72
75
|
raise TimeoutError if @worker.join(@timeout_interval).nil?
|
73
|
-
|
74
|
-
notify_observers(Time.now, @worker[:result], nil)
|
76
|
+
mutex.synchronize { @value = @worker[:result] }
|
75
77
|
rescue Exception => ex
|
76
|
-
|
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
|
data/lib/concurrent/version.rb
CHANGED
data/md/timer_task.md
CHANGED
@@ -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
|
-
|
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
|
8
|
-
can cause the entire thread to abend. In a
|
9
|
-
thread is intended to run for days/weeks/years
|
10
|
-
problem.
|
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
|
15
|
-
the task
|
16
|
-
TimerTask thread can
|
17
|
-
|
18
|
-
to
|
19
|
-
|
20
|
-
One other advantage of
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
24
|
-
task.run!
|
25
|
-
task
|
17
|
+
ScheduledTask.new(1){ fulfilled_value }
|
26
18
|
end
|
27
19
|
|
28
20
|
let(:fulfilled_subject) do
|
29
|
-
|
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
|
-
|
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
|
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:
|
133
|
-
@expected =
|
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(
|
137
|
-
@expected.should eq
|
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.
|
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-
|
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:
|
28
|
-
|
29
|
-
|
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
|
-
|
99
|
-
|
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:
|
120
|
+
rubygems_version: 1.8.24
|
119
121
|
signing_key:
|
120
|
-
specification_version:
|
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
|