concurrent-ruby 0.6.1 → 0.7.0.rc0
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/README.md +1 -1
- data/lib/concurrent.rb +3 -4
- data/lib/concurrent/atomic.rb +46 -0
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
- data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
- data/lib/concurrent/atomic_reference/jruby.rb +8 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
- data/lib/concurrent/atomic_reference/rbx.rb +16 -0
- data/lib/concurrent/atomic_reference/ruby.rb +16 -0
- data/lib/concurrent/atomics.rb +1 -1
- data/lib/concurrent/configuration.rb +1 -1
- data/lib/concurrent/supervisor.rb +1 -1
- data/lib/concurrent/timer_task.rb +0 -36
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +16 -148
- data/lib/concurrent/actor/actor.rb +0 -270
- data/lib/concurrent/actor/postable.rb +0 -102
- data/lib/concurrent/actors.rb +0 -2
- data/lib/concurrent/atomic/atomic.rb +0 -48
- data/lib/concurrent/runnable.rb +0 -90
- data/lib/concurrent/stoppable.rb +0 -20
- data/spec/concurrent/actor/actor_spec.rb +0 -376
- data/spec/concurrent/actor/postable_shared.rb +0 -218
- data/spec/concurrent/actress_spec.rb +0 -211
- data/spec/concurrent/agent_spec.rb +0 -500
- data/spec/concurrent/async_spec.rb +0 -352
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
- data/spec/concurrent/atomic/atomic_spec.rb +0 -133
- data/spec/concurrent/atomic/condition_spec.rb +0 -171
- data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
- data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
- data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
- data/spec/concurrent/atomic/event_spec.rb +0 -200
- data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
- data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
- data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
- data/spec/concurrent/channel/channel_spec.rb +0 -39
- data/spec/concurrent/channel/probe_spec.rb +0 -77
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
- data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
- data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
- data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
- data/spec/concurrent/configuration_spec.rb +0 -69
- data/spec/concurrent/dataflow_spec.rb +0 -242
- data/spec/concurrent/delay_spec.rb +0 -91
- data/spec/concurrent/dereferenceable_shared.rb +0 -146
- data/spec/concurrent/exchanger_spec.rb +0 -66
- data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
- data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
- data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
- data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
- data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
- data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
- data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
- data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
- data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
- data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
- data/spec/concurrent/executor/thread_pool_shared.rb +0 -269
- data/spec/concurrent/executor/timer_set_spec.rb +0 -183
- data/spec/concurrent/future_spec.rb +0 -329
- data/spec/concurrent/ivar_spec.rb +0 -215
- data/spec/concurrent/mvar_spec.rb +0 -380
- data/spec/concurrent/obligation_shared.rb +0 -102
- data/spec/concurrent/obligation_spec.rb +0 -282
- data/spec/concurrent/observable_shared.rb +0 -177
- data/spec/concurrent/observable_spec.rb +0 -56
- data/spec/concurrent/options_parser_spec.rb +0 -71
- data/spec/concurrent/promise_spec.rb +0 -367
- data/spec/concurrent/runnable_shared.rb +0 -68
- data/spec/concurrent/runnable_spec.rb +0 -235
- data/spec/concurrent/scheduled_task_spec.rb +0 -340
- data/spec/concurrent/stoppable_shared.rb +0 -37
- data/spec/concurrent/supervisor_spec.rb +0 -1149
- data/spec/concurrent/timer_task_spec.rb +0 -256
- data/spec/concurrent/tvar_spec.rb +0 -137
- data/spec/concurrent/utility/processor_count_spec.rb +0 -20
- data/spec/concurrent/utility/timeout_spec.rb +0 -50
- data/spec/concurrent/utility/timer_spec.rb +0 -52
- data/spec/spec_helper.rb +0 -41
- data/spec/support/example_group_extensions.rb +0 -52
- data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
@@ -1,270 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'observer'
|
3
|
-
|
4
|
-
require 'concurrent/actor/postable'
|
5
|
-
require 'concurrent/atomic/event'
|
6
|
-
require 'concurrent/obligation'
|
7
|
-
require 'concurrent/runnable'
|
8
|
-
|
9
|
-
module Concurrent
|
10
|
-
|
11
|
-
# Actor-based concurrency is all the rage in some circles. Originally described in
|
12
|
-
# 1973, the actor model is a paradigm for creating asynchronous, concurrent objects
|
13
|
-
# that is becoming increasingly popular. Much has changed since actors were first
|
14
|
-
# written about four decades ago, which has led to a serious fragmentation within
|
15
|
-
# the actor community. There is *no* universally accepted, strict definition of
|
16
|
-
# "actor" and actor implementations differ widely between languages and libraries.
|
17
|
-
#
|
18
|
-
# A good definition of "actor" is:
|
19
|
-
#
|
20
|
-
# An independent, concurrent, single-purpose, computational entity that communicates exclusively via message passing.
|
21
|
-
#
|
22
|
-
# The +Concurrent::Actor+ class in this library is based solely on the
|
23
|
-
# {http://www.scala-lang.org/api/current/index.html#scala.actors.Actor Actor} trait
|
24
|
-
# defined in the Scala standard library. It does not implement all the features of
|
25
|
-
# Scala's +Actor+ but its behavior for what *has* been implemented is nearly identical.
|
26
|
-
# The excluded features mostly deal with Scala's message semantics, strong typing,
|
27
|
-
# and other characteristics of Scala that don't really apply to Ruby.
|
28
|
-
#
|
29
|
-
# Unlike many of the abstractions in this library, +Actor+ takes an *object-oriented*
|
30
|
-
# approach to asynchronous concurrency, rather than a *functional programming*
|
31
|
-
# approach.
|
32
|
-
#
|
33
|
-
# Because +Actor+ mixes in the +Concurrent::Runnable+ module subclasses have access to
|
34
|
-
# the +#on_error+ method and can override it to implement custom error handling. The
|
35
|
-
# +Actor+ base class does not use +#on_error+ so as to avoid conflit with subclasses
|
36
|
-
# which override it. Generally speaking, +#on_error+ should not be used. The +Actor+
|
37
|
-
# base class provides concictent, reliable, and robust error handling already, and
|
38
|
-
# error handling specifics are tied to the message posting method. Incorrect behavior
|
39
|
-
# in an +#on_error+ override can lead to inconsistent +Actor+ behavior that may lead
|
40
|
-
# to confusion and difficult debugging.
|
41
|
-
#
|
42
|
-
# The +Actor+ superclass mixes in the Ruby standard library
|
43
|
-
# {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html Observable}
|
44
|
-
# module to provide consistent callbacks upon message processing completion. The normal
|
45
|
-
# +Observable+ methods, including +#add_observer+ behave normally. Once an observer
|
46
|
-
# is added to an +Actor+ it will be notified of all messages processed *after*
|
47
|
-
# addition. Notification will *not* occur for any messages that have already been
|
48
|
-
# processed.
|
49
|
-
#
|
50
|
-
# Observers will be notified regardless of whether the message processing is successful
|
51
|
-
# or not. The +#update+ method of the observer will receive four arguments. The
|
52
|
-
# appropriate method signature is:
|
53
|
-
#
|
54
|
-
# def update(time, message, result, reason)
|
55
|
-
#
|
56
|
-
# These four arguments represent:
|
57
|
-
#
|
58
|
-
# * The time that message processing was completed
|
59
|
-
# * An array containing all elements of the original message, in order
|
60
|
-
# * The result of the call to +#act+ (will be +nil+ if an exception was raised)
|
61
|
-
# * Any exception raised by +#act+ (or +nil+ if message processing was successful)
|
62
|
-
#
|
63
|
-
# @example Actor Ping Pong
|
64
|
-
# class Ping < Concurrent::Actor
|
65
|
-
#
|
66
|
-
# def initialize(count, pong)
|
67
|
-
# super()
|
68
|
-
# @pong = pong
|
69
|
-
# @remaining = count
|
70
|
-
# end
|
71
|
-
#
|
72
|
-
# def act(msg)
|
73
|
-
#
|
74
|
-
# if msg == :pong
|
75
|
-
# print "Ping: pong\n" if @remaining % 1000 == 0
|
76
|
-
# @pong.post(:ping)
|
77
|
-
#
|
78
|
-
# if @remaining > 0
|
79
|
-
# @pong << :ping
|
80
|
-
# @remaining -= 1
|
81
|
-
# else
|
82
|
-
# print "Ping :stop\n"
|
83
|
-
# @pong << :stop
|
84
|
-
# self.stop
|
85
|
-
# end
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
# end
|
89
|
-
#
|
90
|
-
# class Pong < Concurrent::Actor
|
91
|
-
#
|
92
|
-
# attr_writer :ping
|
93
|
-
#
|
94
|
-
# def initialize
|
95
|
-
# super()
|
96
|
-
# @count = 0
|
97
|
-
# end
|
98
|
-
#
|
99
|
-
# def act(msg)
|
100
|
-
#
|
101
|
-
# if msg == :ping
|
102
|
-
# print "Pong: ping\n" if @count % 1000 == 0
|
103
|
-
# @ping << :pong
|
104
|
-
# @count += 1
|
105
|
-
#
|
106
|
-
# elsif msg == :stop
|
107
|
-
# print "Pong :stop\n"
|
108
|
-
# self.stop
|
109
|
-
# end
|
110
|
-
# end
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# pong = Pong.new
|
114
|
-
# ping = Ping.new(10000, pong)
|
115
|
-
# pong.ping = ping
|
116
|
-
#
|
117
|
-
# t1 = ping.run!
|
118
|
-
# t2 = pong.run!
|
119
|
-
#
|
120
|
-
# ping << :pong
|
121
|
-
#
|
122
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
123
|
-
#
|
124
|
-
# @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
|
125
|
-
class Actor
|
126
|
-
include ::Observable
|
127
|
-
include Postable
|
128
|
-
include Runnable
|
129
|
-
|
130
|
-
private
|
131
|
-
|
132
|
-
# @!visibility private
|
133
|
-
class Poolbox # :nodoc:
|
134
|
-
include Postable
|
135
|
-
|
136
|
-
def initialize(queue)
|
137
|
-
@queue = queue
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
public
|
142
|
-
|
143
|
-
# Create a pool of actors that share a common mailbox.
|
144
|
-
#
|
145
|
-
# Every +Actor+ instance operates on its own thread. When one thread isn't enough capacity
|
146
|
-
# to manage all the messages being sent to an +Actor+ a *pool* can be used instead. A pool
|
147
|
-
# is a collection of +Actor+ instances, all of the same type, that shate a message queue.
|
148
|
-
# Messages from other threads are all sent to a single queue against which all +Actor+s
|
149
|
-
# load balance.
|
150
|
-
#
|
151
|
-
# @param [Integer] count the number of actors in the pool
|
152
|
-
# @param [Array] args zero or more arguments to pass to each actor in the pool
|
153
|
-
#
|
154
|
-
# @return [Array] two-element array with the shared mailbox as the first element
|
155
|
-
# and an array of actors as the second element
|
156
|
-
#
|
157
|
-
# @raise ArgumentError if +count+ is zero or less
|
158
|
-
#
|
159
|
-
# @example
|
160
|
-
# class EchoActor < Concurrent::Actor
|
161
|
-
# def act(*message)
|
162
|
-
# puts "#{message} handled by #{self}"
|
163
|
-
# end
|
164
|
-
# end
|
165
|
-
#
|
166
|
-
# mailbox, pool = EchoActor.pool(5)
|
167
|
-
# pool.each{|echo| echo.run! }
|
168
|
-
#
|
169
|
-
# 10.times{|i| mailbox.post(i) }
|
170
|
-
# #=> [0] handled by #<EchoActor:0x007fc8014fb8b8>
|
171
|
-
# #=> [1] handled by #<EchoActor:0x007fc8014fb890>
|
172
|
-
# #=> [2] handled by #<EchoActor:0x007fc8014fb868>
|
173
|
-
# #=> [3] handled by #<EchoActor:0x007fc8014fb890>
|
174
|
-
# #=> [4] handled by #<EchoActor:0x007fc8014fb840>
|
175
|
-
# #=> [5] handled by #<EchoActor:0x007fc8014fb8b8>
|
176
|
-
# #=> [6] handled by #<EchoActor:0x007fc8014fb8b8>
|
177
|
-
# #=> [7] handled by #<EchoActor:0x007fc8014fb818>
|
178
|
-
# #=> [8] handled by #<EchoActor:0x007fc8014fb890>
|
179
|
-
#
|
180
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
181
|
-
def self.pool(count, *args, &block)
|
182
|
-
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `Actress`.'
|
183
|
-
raise ArgumentError.new('count must be greater than zero') unless count > 0
|
184
|
-
mailbox = Queue.new
|
185
|
-
actors = count.times.collect do
|
186
|
-
if block_given?
|
187
|
-
actor = self.new(*args, &block.dup)
|
188
|
-
else
|
189
|
-
actor = self.new(*args)
|
190
|
-
end
|
191
|
-
actor.instance_variable_set(:@queue, mailbox)
|
192
|
-
actor
|
193
|
-
end
|
194
|
-
return Poolbox.new(mailbox), actors
|
195
|
-
end
|
196
|
-
|
197
|
-
protected
|
198
|
-
|
199
|
-
# Actors are defined by subclassing the +Concurrent::Actor+ class and overriding the
|
200
|
-
# #act method. The #act method can have any signature/arity but +def act(*args)+
|
201
|
-
# is the most flexible and least error-prone signature. The #act method is called in
|
202
|
-
# response to a message being post to the +Actor+ instance (see *Behavior* below).
|
203
|
-
#
|
204
|
-
# @param [Array] message one or more arguments representing the message sent to the
|
205
|
-
# actor via one of the Concurrent::Postable methods
|
206
|
-
#
|
207
|
-
# @return [Object] the result obtained when the message is successfully processed
|
208
|
-
#
|
209
|
-
# @raise NotImplementedError unless overridden in the +Actor+ subclass
|
210
|
-
#
|
211
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
212
|
-
#
|
213
|
-
# @!visibility public
|
214
|
-
def act(*message)
|
215
|
-
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `Actress`.'
|
216
|
-
raise NotImplementedError.new("#{self.class} does not implement #act")
|
217
|
-
end
|
218
|
-
|
219
|
-
# @!visibility private
|
220
|
-
#
|
221
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
222
|
-
def on_run # :nodoc:
|
223
|
-
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `Actress`.'
|
224
|
-
queue.clear
|
225
|
-
end
|
226
|
-
|
227
|
-
# @!visibility private
|
228
|
-
#
|
229
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
230
|
-
def on_stop # :nodoc:
|
231
|
-
queue.clear
|
232
|
-
queue.push(:stop)
|
233
|
-
end
|
234
|
-
|
235
|
-
# @!visibility private
|
236
|
-
#
|
237
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
238
|
-
def on_task # :nodoc:
|
239
|
-
package = queue.pop
|
240
|
-
return if package == :stop
|
241
|
-
result = ex = nil
|
242
|
-
notifier = package.notifier
|
243
|
-
begin
|
244
|
-
if notifier.nil? || (notifier.is_a?(Event) && ! notifier.set?)
|
245
|
-
result = act(*package.message)
|
246
|
-
end
|
247
|
-
rescue => ex
|
248
|
-
on_error(Time.now, package.message, ex)
|
249
|
-
ensure
|
250
|
-
if notifier.is_a?(Event) && ! notifier.set?
|
251
|
-
package.handler.push(result || ex)
|
252
|
-
package.notifier.set
|
253
|
-
elsif package.handler.is_a?(IVar)
|
254
|
-
package.handler.complete(! result.nil?, result, ex)
|
255
|
-
elsif package.handler.respond_to?(:post) && ex.nil?
|
256
|
-
package.handler.post(result)
|
257
|
-
end
|
258
|
-
|
259
|
-
changed
|
260
|
-
notify_observers(Time.now, package.message, result, ex)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
# @!visibility private
|
265
|
-
#
|
266
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
267
|
-
def on_error(time, msg, ex) # :nodoc:
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/event'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
module Postable
|
6
|
-
|
7
|
-
# @!visibility private
|
8
|
-
Package = Struct.new(:message, :handler, :notifier) # :nodoc:
|
9
|
-
|
10
|
-
# Sends a message to and returns. It's a fire-and-forget interaction.
|
11
|
-
#
|
12
|
-
# @param [Array] message one or more arguments representing a single message
|
13
|
-
# to be sent to the receiver.
|
14
|
-
#
|
15
|
-
# @return [Object] false when the message cannot be queued else the number
|
16
|
-
# of messages in the queue *after* this message has been post
|
17
|
-
#
|
18
|
-
# @raise ArgumentError when the message is empty
|
19
|
-
#
|
20
|
-
# @example
|
21
|
-
# class EchoActor < Concurrent::Actor
|
22
|
-
# def act(*message)
|
23
|
-
# p message
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# echo = EchoActor.new
|
28
|
-
# echo.run!
|
29
|
-
#
|
30
|
-
# echo.post("Don't panic") #=> true
|
31
|
-
# #=> ["Don't panic"]
|
32
|
-
#
|
33
|
-
# echo.post(1, 2, 3, 4, 5) #=> true
|
34
|
-
# #=> [1, 2, 3, 4, 5]
|
35
|
-
#
|
36
|
-
# echo << "There's a frood who really knows where his towel is." #=> #<EchoActor:0x007fc8012b8448...
|
37
|
-
# #=> ["There's a frood who really knows where his towel is."]
|
38
|
-
def post(*message)
|
39
|
-
raise ArgumentError.new('empty message') if message.empty?
|
40
|
-
return false unless ready?
|
41
|
-
queue.push(Package.new(message))
|
42
|
-
true
|
43
|
-
end
|
44
|
-
|
45
|
-
def <<(message)
|
46
|
-
post(*message)
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
def post?(*message)
|
51
|
-
raise ArgumentError.new('empty message') if message.empty?
|
52
|
-
return nil unless ready?
|
53
|
-
ivar = IVar.new
|
54
|
-
queue.push(Package.new(message, ivar))
|
55
|
-
ivar
|
56
|
-
end
|
57
|
-
|
58
|
-
def post!(seconds, *message)
|
59
|
-
raise ArgumentError.new('empty message') if message.empty?
|
60
|
-
raise Concurrent::LifecycleError unless ready?
|
61
|
-
raise Concurrent::TimeoutError if seconds.to_f <= 0.0
|
62
|
-
event = Event.new
|
63
|
-
cback = Queue.new
|
64
|
-
queue.push(Package.new(message, cback, event))
|
65
|
-
if event.wait(seconds)
|
66
|
-
result = cback.pop
|
67
|
-
if result.is_a?(Exception)
|
68
|
-
raise result
|
69
|
-
else
|
70
|
-
return result
|
71
|
-
end
|
72
|
-
else
|
73
|
-
event.set # attempt to cancel
|
74
|
-
raise Concurrent::TimeoutError
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
79
|
-
def forward(receiver, *message)
|
80
|
-
raise ArgumentError.new('empty message') if message.empty?
|
81
|
-
return false unless ready?
|
82
|
-
queue.push(Package.new(message, receiver))
|
83
|
-
queue.length
|
84
|
-
end
|
85
|
-
|
86
|
-
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
87
|
-
def ready?
|
88
|
-
if self.respond_to?(:running?) && ! running?
|
89
|
-
false
|
90
|
-
else
|
91
|
-
true
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
private
|
96
|
-
|
97
|
-
# @!visibility private
|
98
|
-
def queue # :nodoc:
|
99
|
-
@queue ||= Queue.new
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
data/lib/concurrent/actors.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
class MutexAtomic
|
4
|
-
|
5
|
-
def initialize(init = nil)
|
6
|
-
@value = init
|
7
|
-
@mutex = Mutex.new
|
8
|
-
end
|
9
|
-
|
10
|
-
def value
|
11
|
-
@mutex.lock
|
12
|
-
@value
|
13
|
-
ensure
|
14
|
-
@mutex.unlock
|
15
|
-
end
|
16
|
-
|
17
|
-
def value=(value)
|
18
|
-
@mutex.lock
|
19
|
-
@value = value
|
20
|
-
ensure
|
21
|
-
@mutex.unlock
|
22
|
-
end
|
23
|
-
|
24
|
-
def modify
|
25
|
-
@mutex.lock
|
26
|
-
result = yield @value
|
27
|
-
@value = result
|
28
|
-
ensure
|
29
|
-
@mutex.unlock
|
30
|
-
end
|
31
|
-
|
32
|
-
def compare_and_set(expect, update)
|
33
|
-
@mutex.lock
|
34
|
-
if @value == expect
|
35
|
-
@value = update
|
36
|
-
true
|
37
|
-
else
|
38
|
-
false
|
39
|
-
end
|
40
|
-
ensure
|
41
|
-
@mutex.unlock
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class Atomic < MutexAtomic
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
data/lib/concurrent/runnable.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/errors'
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
|
7
|
-
module Runnable
|
8
|
-
|
9
|
-
class Context
|
10
|
-
attr_reader :runner, :thread
|
11
|
-
def initialize(runner)
|
12
|
-
@runner = runner
|
13
|
-
@thread = Thread.new(runner) do |runner|
|
14
|
-
Thread.abort_on_exception = false
|
15
|
-
runner.run
|
16
|
-
end
|
17
|
-
@thread.join(0.1) # let the thread start
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.included(base)
|
22
|
-
|
23
|
-
class << base
|
24
|
-
|
25
|
-
def run!(*args, &block)
|
26
|
-
runner = self.new(*args, &block)
|
27
|
-
return Context.new(runner)
|
28
|
-
rescue => ex
|
29
|
-
return nil
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def run!(abort_on_exception = false)
|
35
|
-
raise LifecycleError.new('already running') if @running
|
36
|
-
thread = Thread.new do
|
37
|
-
Thread.current.abort_on_exception = abort_on_exception
|
38
|
-
self.run
|
39
|
-
end
|
40
|
-
thread.join(0.1) # let the thread start
|
41
|
-
return thread
|
42
|
-
end
|
43
|
-
|
44
|
-
def run
|
45
|
-
mutex.synchronize do
|
46
|
-
raise LifecycleError.new('already running') if @running
|
47
|
-
raise LifecycleError.new('#on_task not implemented') unless self.respond_to?(:on_task, true)
|
48
|
-
on_run if respond_to?(:on_run, true)
|
49
|
-
@running = true
|
50
|
-
end
|
51
|
-
|
52
|
-
loop do
|
53
|
-
break unless @running
|
54
|
-
on_task
|
55
|
-
break unless @running
|
56
|
-
Thread.pass
|
57
|
-
end
|
58
|
-
|
59
|
-
after_run if respond_to?(:after_run, true)
|
60
|
-
return true
|
61
|
-
rescue LifecycleError => ex
|
62
|
-
@running = false
|
63
|
-
raise ex
|
64
|
-
rescue => ex
|
65
|
-
@running = false
|
66
|
-
return false
|
67
|
-
end
|
68
|
-
|
69
|
-
def stop
|
70
|
-
return true unless @running
|
71
|
-
mutex.synchronize do
|
72
|
-
@running = false
|
73
|
-
on_stop if respond_to?(:on_stop, true)
|
74
|
-
end
|
75
|
-
return true
|
76
|
-
rescue => ex
|
77
|
-
return false
|
78
|
-
end
|
79
|
-
|
80
|
-
def running?
|
81
|
-
return @running == true
|
82
|
-
end
|
83
|
-
|
84
|
-
protected
|
85
|
-
|
86
|
-
def mutex
|
87
|
-
@mutex ||= Mutex.new
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|