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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/concurrent.rb +3 -4
  4. data/lib/concurrent/atomic.rb +46 -0
  5. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  6. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  7. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  8. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  9. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  10. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  11. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  12. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  13. data/lib/concurrent/atomics.rb +1 -1
  14. data/lib/concurrent/configuration.rb +1 -1
  15. data/lib/concurrent/supervisor.rb +1 -1
  16. data/lib/concurrent/timer_task.rb +0 -36
  17. data/lib/concurrent/version.rb +1 -1
  18. data/lib/concurrent_ruby_ext.so +0 -0
  19. data/lib/extension_helper.rb +9 -0
  20. metadata +16 -148
  21. data/lib/concurrent/actor/actor.rb +0 -270
  22. data/lib/concurrent/actor/postable.rb +0 -102
  23. data/lib/concurrent/actors.rb +0 -2
  24. data/lib/concurrent/atomic/atomic.rb +0 -48
  25. data/lib/concurrent/runnable.rb +0 -90
  26. data/lib/concurrent/stoppable.rb +0 -20
  27. data/spec/concurrent/actor/actor_spec.rb +0 -376
  28. data/spec/concurrent/actor/postable_shared.rb +0 -218
  29. data/spec/concurrent/actress_spec.rb +0 -211
  30. data/spec/concurrent/agent_spec.rb +0 -500
  31. data/spec/concurrent/async_spec.rb +0 -352
  32. data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
  33. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
  34. data/spec/concurrent/atomic/atomic_spec.rb +0 -133
  35. data/spec/concurrent/atomic/condition_spec.rb +0 -171
  36. data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
  37. data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
  38. data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
  39. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
  40. data/spec/concurrent/atomic/event_spec.rb +0 -200
  41. data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
  42. data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
  43. data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
  44. data/spec/concurrent/channel/channel_spec.rb +0 -39
  45. data/spec/concurrent/channel/probe_spec.rb +0 -77
  46. data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
  47. data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
  48. data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
  49. data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
  50. data/spec/concurrent/configuration_spec.rb +0 -69
  51. data/spec/concurrent/dataflow_spec.rb +0 -242
  52. data/spec/concurrent/delay_spec.rb +0 -91
  53. data/spec/concurrent/dereferenceable_shared.rb +0 -146
  54. data/spec/concurrent/exchanger_spec.rb +0 -66
  55. data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
  56. data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
  57. data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
  58. data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
  59. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
  60. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
  61. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
  62. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
  63. data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
  64. data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
  65. data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
  66. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
  67. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
  68. data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
  69. data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
  70. data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
  71. data/spec/concurrent/executor/thread_pool_shared.rb +0 -269
  72. data/spec/concurrent/executor/timer_set_spec.rb +0 -183
  73. data/spec/concurrent/future_spec.rb +0 -329
  74. data/spec/concurrent/ivar_spec.rb +0 -215
  75. data/spec/concurrent/mvar_spec.rb +0 -380
  76. data/spec/concurrent/obligation_shared.rb +0 -102
  77. data/spec/concurrent/obligation_spec.rb +0 -282
  78. data/spec/concurrent/observable_shared.rb +0 -177
  79. data/spec/concurrent/observable_spec.rb +0 -56
  80. data/spec/concurrent/options_parser_spec.rb +0 -71
  81. data/spec/concurrent/promise_spec.rb +0 -367
  82. data/spec/concurrent/runnable_shared.rb +0 -68
  83. data/spec/concurrent/runnable_spec.rb +0 -235
  84. data/spec/concurrent/scheduled_task_spec.rb +0 -340
  85. data/spec/concurrent/stoppable_shared.rb +0 -37
  86. data/spec/concurrent/supervisor_spec.rb +0 -1149
  87. data/spec/concurrent/timer_task_spec.rb +0 -256
  88. data/spec/concurrent/tvar_spec.rb +0 -137
  89. data/spec/concurrent/utility/processor_count_spec.rb +0 -20
  90. data/spec/concurrent/utility/timeout_spec.rb +0 -50
  91. data/spec/concurrent/utility/timer_spec.rb +0 -52
  92. data/spec/spec_helper.rb +0 -41
  93. data/spec/support/example_group_extensions.rb +0 -52
  94. 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
@@ -1,2 +0,0 @@
1
- require 'concurrent/actor/actor'
2
- require 'concurrent/actor/postable'
@@ -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
@@ -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