concurrent-ruby 0.6.0.pre.2 → 0.6.0

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -8
  3. data/lib/concurrent.rb +2 -0
  4. data/lib/concurrent/actor/actor.rb +3 -3
  5. data/lib/concurrent/actor/postable.rb +1 -1
  6. data/lib/concurrent/actors.rb +0 -3
  7. data/lib/concurrent/actress.rb +75 -0
  8. data/lib/concurrent/actress/ad_hoc.rb +14 -0
  9. data/lib/concurrent/actress/context.rb +96 -0
  10. data/lib/concurrent/actress/core.rb +204 -0
  11. data/lib/concurrent/actress/core_delegations.rb +37 -0
  12. data/lib/concurrent/actress/doc.md +53 -0
  13. data/lib/concurrent/actress/envelope.rb +25 -0
  14. data/lib/concurrent/actress/errors.rb +14 -0
  15. data/lib/concurrent/actress/reference.rb +64 -0
  16. data/lib/concurrent/actress/type_check.rb +48 -0
  17. data/lib/concurrent/agent.rb +20 -11
  18. data/lib/concurrent/async.rb +54 -25
  19. data/lib/concurrent/atomic/atomic.rb +48 -0
  20. data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
  21. data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
  22. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
  23. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
  24. data/lib/concurrent/atomic/event.rb +11 -16
  25. data/lib/concurrent/atomics.rb +1 -0
  26. data/lib/concurrent/channel/channel.rb +4 -2
  27. data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
  28. data/lib/concurrent/configuration.rb +59 -47
  29. data/lib/concurrent/delay.rb +28 -12
  30. data/lib/concurrent/dereferenceable.rb +6 -6
  31. data/lib/concurrent/errors.rb +30 -0
  32. data/lib/concurrent/executor/executor.rb +11 -4
  33. data/lib/concurrent/executor/immediate_executor.rb +1 -0
  34. data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
  35. data/lib/concurrent/executor/one_by_one.rb +24 -12
  36. data/lib/concurrent/executor/per_thread_executor.rb +1 -0
  37. data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
  38. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
  39. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
  40. data/lib/concurrent/executor/timer_set.rb +1 -1
  41. data/lib/concurrent/future.rb +0 -2
  42. data/lib/concurrent/ivar.rb +31 -6
  43. data/lib/concurrent/logging.rb +17 -0
  44. data/lib/concurrent/mvar.rb +45 -0
  45. data/lib/concurrent/obligation.rb +61 -20
  46. data/lib/concurrent/observable.rb +7 -0
  47. data/lib/concurrent/promise.rb +1 -0
  48. data/lib/concurrent/runnable.rb +2 -2
  49. data/lib/concurrent/supervisor.rb +1 -2
  50. data/lib/concurrent/timer_task.rb +17 -13
  51. data/lib/concurrent/tvar.rb +113 -73
  52. data/lib/concurrent/utility/processor_count.rb +141 -116
  53. data/lib/concurrent/utility/timeout.rb +4 -5
  54. data/lib/concurrent/version.rb +1 -1
  55. data/spec/concurrent/actor/postable_shared.rb +1 -1
  56. data/spec/concurrent/actress_spec.rb +191 -0
  57. data/spec/concurrent/async_spec.rb +35 -3
  58. data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
  59. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
  60. data/spec/concurrent/atomic/atomic_spec.rb +133 -0
  61. data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
  62. data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
  63. data/spec/concurrent/configuration_spec.rb +5 -2
  64. data/spec/concurrent/delay_spec.rb +14 -0
  65. data/spec/concurrent/exchanger_spec.rb +4 -9
  66. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
  67. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
  68. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
  69. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
  70. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
  71. data/spec/concurrent/ivar_spec.rb +2 -2
  72. data/spec/concurrent/obligation_spec.rb +113 -24
  73. data/spec/concurrent/observable_shared.rb +4 -0
  74. data/spec/concurrent/observable_spec.rb +8 -3
  75. data/spec/concurrent/runnable_spec.rb +2 -2
  76. data/spec/concurrent/scheduled_task_spec.rb +1 -0
  77. data/spec/concurrent/supervisor_spec.rb +26 -11
  78. data/spec/concurrent/timer_task_spec.rb +36 -35
  79. data/spec/concurrent/tvar_spec.rb +1 -1
  80. data/spec/concurrent/utility/timer_spec.rb +8 -8
  81. data/spec/spec_helper.rb +8 -18
  82. data/spec/support/example_group_extensions.rb +48 -0
  83. metadata +23 -16
  84. data/lib/concurrent/actor/actor_context.rb +0 -77
  85. data/lib/concurrent/actor/actor_ref.rb +0 -67
  86. data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
  87. data/lib/concurrent_ruby_ext.bundle +0 -0
  88. data/spec/concurrent/actor/actor_context_spec.rb +0 -29
  89. data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
  90. data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
  91. data/spec/support/functions.rb +0 -25
@@ -0,0 +1,48 @@
1
+ require 'rbconfig'
2
+
3
+ module Concurrent
4
+ module TestHelpers
5
+ def delta(v1, v2)
6
+ if block_given?
7
+ v1 = yield(v1)
8
+ v2 = yield(v2)
9
+ end
10
+ return (v1 - v2).abs
11
+ end
12
+
13
+ def mri?
14
+ RbConfig::CONFIG['ruby_install_name']=~ /^ruby$/i
15
+ end
16
+
17
+ def jruby?
18
+ RbConfig::CONFIG['ruby_install_name']=~ /^jruby$/i
19
+ end
20
+
21
+ def rbx?
22
+ RbConfig::CONFIG['ruby_install_name']=~ /^rbx$/i
23
+ end
24
+
25
+ def reset_gem_configuration
26
+ Concurrent.instance_variable_set(:@configuration, Concurrent::Configuration.new)
27
+ end
28
+
29
+ extend self
30
+ end
31
+ end
32
+
33
+ class RSpec::Core::ExampleGroup
34
+ def self.with_full_reset
35
+ before(:each) do
36
+ reset_gem_configuration
37
+ end
38
+
39
+ after(:each) do
40
+ Thread.list.each do |thread|
41
+ thread.kill unless thread == Thread.current
42
+ end
43
+ end
44
+ end
45
+
46
+ include Concurrent::TestHelpers
47
+ extend Concurrent::TestHelpers
48
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0.pre.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerry D'Antonio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-12 00:00:00.000000000 Z
11
+ date: 2014-05-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
@@ -24,13 +24,21 @@ files:
24
24
  - README.md
25
25
  - lib/concurrent.rb
26
26
  - lib/concurrent/actor/actor.rb
27
- - lib/concurrent/actor/actor_context.rb
28
- - lib/concurrent/actor/actor_ref.rb
29
27
  - lib/concurrent/actor/postable.rb
30
- - lib/concurrent/actor/simple_actor_ref.rb
31
28
  - lib/concurrent/actors.rb
29
+ - lib/concurrent/actress.rb
30
+ - lib/concurrent/actress/ad_hoc.rb
31
+ - lib/concurrent/actress/context.rb
32
+ - lib/concurrent/actress/core.rb
33
+ - lib/concurrent/actress/core_delegations.rb
34
+ - lib/concurrent/actress/doc.md
35
+ - lib/concurrent/actress/envelope.rb
36
+ - lib/concurrent/actress/errors.rb
37
+ - lib/concurrent/actress/reference.rb
38
+ - lib/concurrent/actress/type_check.rb
32
39
  - lib/concurrent/agent.rb
33
40
  - lib/concurrent/async.rb
41
+ - lib/concurrent/atomic/atomic.rb
34
42
  - lib/concurrent/atomic/atomic_boolean.rb
35
43
  - lib/concurrent/atomic/atomic_fixnum.rb
36
44
  - lib/concurrent/atomic/condition.rb
@@ -54,6 +62,7 @@ files:
54
62
  - lib/concurrent/dataflow.rb
55
63
  - lib/concurrent/delay.rb
56
64
  - lib/concurrent/dereferenceable.rb
65
+ - lib/concurrent/errors.rb
57
66
  - lib/concurrent/exchanger.rb
58
67
  - lib/concurrent/executor/cached_thread_pool.rb
59
68
  - lib/concurrent/executor/executor.rb
@@ -77,6 +86,7 @@ files:
77
86
  - lib/concurrent/executors.rb
78
87
  - lib/concurrent/future.rb
79
88
  - lib/concurrent/ivar.rb
89
+ - lib/concurrent/logging.rb
80
90
  - lib/concurrent/mvar.rb
81
91
  - lib/concurrent/obligation.rb
82
92
  - lib/concurrent/observable.rb
@@ -94,16 +104,14 @@ files:
94
104
  - lib/concurrent/utility/timer.rb
95
105
  - lib/concurrent/version.rb
96
106
  - lib/concurrent_ruby.rb
97
- - lib/concurrent_ruby_ext.bundle
98
- - spec/concurrent/actor/actor_context_spec.rb
99
- - spec/concurrent/actor/actor_ref_shared.rb
100
107
  - spec/concurrent/actor/actor_spec.rb
101
108
  - spec/concurrent/actor/postable_shared.rb
102
- - spec/concurrent/actor/simple_actor_ref_spec.rb
109
+ - spec/concurrent/actress_spec.rb
103
110
  - spec/concurrent/agent_spec.rb
104
111
  - spec/concurrent/async_spec.rb
105
112
  - spec/concurrent/atomic/atomic_boolean_spec.rb
106
113
  - spec/concurrent/atomic/atomic_fixnum_spec.rb
114
+ - spec/concurrent/atomic/atomic_spec.rb
107
115
  - spec/concurrent/atomic/condition_spec.rb
108
116
  - spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb
109
117
  - spec/concurrent/atomic/copy_on_write_observer_set_spec.rb
@@ -162,7 +170,7 @@ files:
162
170
  - spec/concurrent/utility/timeout_spec.rb
163
171
  - spec/concurrent/utility/timer_spec.rb
164
172
  - spec/spec_helper.rb
165
- - spec/support/functions.rb
173
+ - spec/support/example_group_extensions.rb
166
174
  - spec/support/less_than_or_equal_to_matcher.rb
167
175
  homepage: http://www.concurrent-ruby.com
168
176
  licenses:
@@ -179,9 +187,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
187
  version: 1.9.3
180
188
  required_rubygems_version: !ruby/object:Gem::Requirement
181
189
  requirements:
182
- - - ">"
190
+ - - ">="
183
191
  - !ruby/object:Gem::Version
184
- version: 1.3.1
192
+ version: '0'
185
193
  requirements: []
186
194
  rubyforge_project:
187
195
  rubygems_version: 2.2.2
@@ -190,15 +198,14 @@ specification_version: 4
190
198
  summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
191
199
  F#, C#, Java, and classic concurrency patterns.
192
200
  test_files:
193
- - spec/concurrent/actor/actor_context_spec.rb
194
- - spec/concurrent/actor/actor_ref_shared.rb
195
201
  - spec/concurrent/actor/actor_spec.rb
196
202
  - spec/concurrent/actor/postable_shared.rb
197
- - spec/concurrent/actor/simple_actor_ref_spec.rb
203
+ - spec/concurrent/actress_spec.rb
198
204
  - spec/concurrent/agent_spec.rb
199
205
  - spec/concurrent/async_spec.rb
200
206
  - spec/concurrent/atomic/atomic_boolean_spec.rb
201
207
  - spec/concurrent/atomic/atomic_fixnum_spec.rb
208
+ - spec/concurrent/atomic/atomic_spec.rb
202
209
  - spec/concurrent/atomic/condition_spec.rb
203
210
  - spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb
204
211
  - spec/concurrent/atomic/copy_on_write_observer_set_spec.rb
@@ -257,6 +264,6 @@ test_files:
257
264
  - spec/concurrent/utility/timeout_spec.rb
258
265
  - spec/concurrent/utility/timer_spec.rb
259
266
  - spec/spec_helper.rb
260
- - spec/support/functions.rb
267
+ - spec/support/example_group_extensions.rb
261
268
  - spec/support/less_than_or_equal_to_matcher.rb
262
269
  has_rdoc:
@@ -1,77 +0,0 @@
1
- require 'concurrent/actor/simple_actor_ref'
2
-
3
- module Concurrent
4
-
5
- # Actor-based concurrency is all the rage in some circles. Originally described in
6
- # 1973, the actor model is a paradigm for creating asynchronous, concurrent objects
7
- # that is becoming increasingly popular. Much has changed since actors were first
8
- # written about four decades ago, which has led to a serious fragmentation within
9
- # the actor community. There is *no* universally accepted, strict definition of
10
- # "actor" and actor implementations differ widely between languages and libraries.
11
- #
12
- # A good definition of "actor" is:
13
- #
14
- # An independent, concurrent, single-purpose, computational entity that communicates exclusively via message passing.
15
- #
16
- # The actor framework in this library is heavily influenced by the Akka toolkit,
17
- # with additional inspiration from Erlang and Scala. Unlike many of the abstractions
18
- # in this library, `ActorContext` takes an *object-oriented* approach to asynchronous
19
- # concurrency, rather than a *functional programming* approach.
20
- #
21
- # Creating an actor class is achieved by including the `ActorContext` module
22
- # within a standard Ruby class. One `ActorContext` is mixed in, however, everything
23
- # changes. Objects of the class can no longer be instanced with the `#new` method.
24
- # Instead, the various factor methods, such as `#spawn`, must be used. These factory
25
- # methods do not return direct references to the actor object. Instead they return
26
- # objects of one of the `ActorRef` subclasses. The `ActorRef` objects encapsulate
27
- # actor objects. This encapsulation is necessary to prevent synchronous method calls
28
- # froom being made against the actors. It also allows the messaging and lifecycle
29
- # behavior to be implemented within the `ActorRef` subclasses, allowing for better
30
- # separation of responsibility.
31
- #
32
- # @see Concurrent::ActorRef
33
- #
34
- # @see http://akka.io/
35
- # @see http://www.erlang.org/doc/getting_started/conc_prog.html
36
- # @see http://www.scala-lang.org/api/current/index.html#scala.actors.Actor
37
- #
38
- # @see http://doc.akka.io/docs/akka/snapshot/general/supervision.html#What_Restarting_Means What Restarting Means
39
- # @see http://doc.akka.io/docs/akka/snapshot/general/supervision.html#What_Lifecycle_Monitoring_Means What Lifecycle Monitoring Means
40
- module ActorContext
41
-
42
- # Callback method called by the `ActorRef` which encapsulates the actor instance.
43
- def on_start
44
- end
45
-
46
- # Callback method called by the `ActorRef` which encapsulates the actor instance.
47
- def on_shutdown
48
- end
49
-
50
- # Callback method called by the `ActorRef` which encapsulates the actor instance.
51
- #
52
- # @param [Time] time the date/time at which the error occurred
53
- # @param [Array] message the message that caused the error
54
- # @param [Exception] exception the exception object that was raised
55
- def on_error(time, message, exception)
56
- end
57
-
58
- def self.included(base)
59
-
60
- class << base
61
-
62
- # Create a single, unregistered actor. The actor will run on its own, dedicated
63
- # thread. The thread will be started the first time a message is post to the actor.
64
- # Should the thread ever die it will be restarted the next time a message is post.
65
- #
66
- # @param [Hash] opts the options defining actor behavior
67
- # @option opts [Array] :args (`nil`) arguments to be passed to the actor constructor
68
- #
69
- # @return [SimpleActorRef] the `ActorRef` encapsulating the actor
70
- def spawn(opts = {})
71
- args = opts.fetch(:args, [])
72
- Concurrent::SimpleActorRef.new(self.new(*args), opts)
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,67 +0,0 @@
1
- module Concurrent
2
-
3
- # Base class for classes that encapsulate `ActorContext` objects.
4
- #
5
- # @see Concurrent::ActorContext
6
- module ActorRef
7
-
8
- # @!method post(*msg, &block)
9
- #
10
- # Send a message and return a future which will eventually be updated
11
- # with the result of the operation. An optional callback block can be
12
- # given which will be called once the operation is complete. Although
13
- # it is possible to use a callback block and also interrogate the
14
- # returned future it is a good practice to do one or the other.
15
- #
16
- # @param [Array] msg One or more elements of the message
17
- #
18
- # @yield a callback operation to be performed when the operation is complete.
19
- # @yieldparam [Time] time the date/time at which the error occurred
20
- # @yieldparam [Object] result the result of message processing or `nil` on error
21
- # @yieldparam [Exception] exception the exception object that was raised or `nil` on success
22
- #
23
- # @return [IVar] a future that will eventually contain the result of message processing
24
-
25
- # @!method post!(timeout, *msg)
26
- # Send a message synchronously and block awaiting the response.
27
- #
28
- # @param [Integer] timeout the maximum number of seconds to block waiting
29
- # for a response
30
- # @param [Array] msg One or more elements of the message
31
- #
32
- # @return [Object] the result of successful message processing
33
- #
34
- # @raise [Concurrent::TimeoutError] if a timeout occurs
35
- # @raise [Exception] an exception which occurred during message processing
36
-
37
- # @!method running?()
38
- # Is the actor running and processing messages?
39
- # @return [Boolean] `true` if running else `false`
40
-
41
- # @!method shutdown?()
42
- # Is the actor shutdown and no longer processing messages?
43
- # @return [Boolean] `true` if shutdown else `false`
44
-
45
- # @!method shutdown()
46
- # Shutdown the actor, gracefully exit all threads, and stop processing messages.
47
- # @return [Boolean] `true` if shutdown is successful else `false`
48
-
49
- # @!method join(limit = nil)
50
- # Suspend the current thread until the actor has been shutdown
51
- # @param [Integer] limit the maximum number of seconds to block waiting for the
52
- # actor to shutdown. Block indefinitely when `nil` or not given
53
- # @return [Boolean] `true` if the actor shutdown before the limit expired else `false`
54
- # @see http://www.ruby-doc.org/core-2.1.1/Thread.html#method-i-join
55
-
56
- # Send a message and return. It's a fire-and-forget interaction.
57
- #
58
- # @param [Object] message a single value representing the message to be sent
59
- # to the actor or an array of multiple message components
60
- #
61
- # @return [ActorRef] self
62
- def <<(message)
63
- post(*message)
64
- self
65
- end
66
- end
67
- end
@@ -1,94 +0,0 @@
1
- require 'concurrent/actor/actor_ref'
2
- require 'concurrent/atomic/event'
3
- require 'concurrent/executor/single_thread_executor'
4
- require 'concurrent/ivar'
5
-
6
- module Concurrent
7
-
8
- class SimpleActorRef
9
- include ActorRef
10
-
11
- def initialize(actor, opts = {})
12
- @actor = actor
13
- @mutex = Mutex.new
14
- @one_by_one = OneByOne.new
15
- @executor = OptionsParser::get_executor_from(opts)
16
- @stop_event = Event.new
17
- @reset_on_error = opts.fetch(:reset_on_error, true)
18
- @exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError
19
- @args = opts.fetch(:args, []) if @reset_on_error
20
-
21
- @actor.define_singleton_method(:shutdown, &method(:set_stop_event))
22
- @actor.on_start
23
- end
24
-
25
- def running?
26
- not @stop_event.set?
27
- end
28
-
29
- def shutdown?
30
- @stop_event.set?
31
- end
32
-
33
- def post(*msg, &block)
34
- raise ArgumentError.new('message cannot be empty') if msg.empty?
35
- ivar = IVar.new
36
- @one_by_one.post(@executor, Message.new(msg, ivar, block), &method(:process_message))
37
- ivar
38
- end
39
-
40
- def post!(timeout, *msg)
41
- raise Concurrent::TimeoutError unless timeout.nil? || timeout >= 0
42
- ivar = self.post(*msg)
43
- ivar.value(timeout)
44
- if ivar.incomplete?
45
- raise Concurrent::TimeoutError
46
- elsif ivar.reason
47
- raise ivar.reason
48
- end
49
- ivar.value
50
- end
51
-
52
- def shutdown
53
- @mutex.synchronize do
54
- return if shutdown?
55
- @actor.on_shutdown
56
- @stop_event.set
57
- end
58
- end
59
-
60
- def join(limit = nil)
61
- @stop_event.wait(limit)
62
- end
63
-
64
- private
65
-
66
- Message = Struct.new(:payload, :ivar, :callback)
67
-
68
- def set_stop_event
69
- @stop_event.set
70
- end
71
-
72
- def process_message(message)
73
- result = ex = nil
74
-
75
- begin
76
- result = @actor.receive(*message.payload)
77
- rescue @exception_class => ex
78
- @actor.on_error(Time.now, message.payload, ex)
79
- if @reset_on_error
80
- @mutex.synchronize{ @actor = @actor.class.new(*@args) }
81
- end
82
- ensure
83
- now = Time.now
84
- message.ivar.complete(ex.nil?, result, ex)
85
-
86
- begin
87
- message.callback.call(now, result, ex) if message.callback
88
- rescue @exception_class => ex
89
- # suppress
90
- end
91
- end
92
- end
93
- end
94
- end
Binary file
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Concurrent
4
-
5
- describe ActorContext do
6
-
7
- let(:described_class) do
8
- Class.new do
9
- include ActorContext
10
- end
11
- end
12
-
13
- context 'callbacks' do
14
-
15
- subject { described_class.send(:new) }
16
-
17
- specify { subject.should respond_to :on_start }
18
-
19
- specify { subject.should respond_to :on_shutdown }
20
- end
21
-
22
- context '#spawn' do
23
-
24
- it 'returns an ActorRef' do
25
- described_class.spawn.should be_a ActorRef
26
- end
27
- end
28
- end
29
- end
@@ -1,263 +0,0 @@
1
- require 'spec_helper'
2
-
3
- def create_actor_test_class
4
- Class.new do
5
- include Concurrent::ActorContext
6
-
7
- attr_reader :argv
8
-
9
- def initialize(*args)
10
- @argv = args
11
- end
12
-
13
- def receive(*msg)
14
- case msg.first
15
- when :poison
16
- raise StandardError
17
- when :bullet
18
- raise Exception
19
- when :stop
20
- shutdown
21
- when :terminate
22
- Thread.current.kill
23
- when :sleep
24
- sleep(msg.last)
25
- when :check
26
- msg[1].set(msg.last)
27
- else
28
- msg.first
29
- end
30
- end
31
- end
32
- end
33
-
34
- share_examples_for :actor_ref do
35
-
36
- after(:each) do
37
- subject.shutdown
38
- end
39
-
40
- it 'includes ActorRef' do
41
- subject.should be_a Concurrent::ActorRef
42
- end
43
-
44
- context 'running and shutdown' do
45
-
46
- specify { subject.should respond_to :shutdown }
47
-
48
- specify { subject.should be_running }
49
-
50
- specify { subject.should_not be_shutdown }
51
-
52
- specify do
53
- subject.shutdown
54
- sleep(0.1)
55
- subject.should be_shutdown
56
- end
57
-
58
- it 'defines a shutdown method on the actor(s)' do
59
- subject << :foo
60
- subject << :stop
61
- sleep(0.1)
62
- subject.should be_shutdown
63
- end
64
- end
65
-
66
- context '#post' do
67
-
68
- it 'raises an exception when the message is empty' do
69
- expect {
70
- subject.post
71
- }.to raise_error(ArgumentError)
72
- end
73
-
74
- it 'returns an IVar' do
75
- subject.post(:foo).should be_a Concurrent::IVar
76
- end
77
-
78
- it 'fulfills the IVar when message is processed' do
79
- ivar = subject.post(:foo)
80
- sleep(0.1)
81
- ivar.should be_fulfilled
82
- ivar.value.should eq :foo
83
- end
84
-
85
- it 'rejects the IVar when message processing fails' do
86
- ivar = subject.post(:poison)
87
- sleep(0.1)
88
- ivar.should be_rejected
89
- ivar.reason.should be_a StandardError
90
- end
91
- end
92
-
93
- context '#<<' do
94
-
95
- it 'posts the message' do
96
- ivar = Concurrent::IVar.new
97
- subject << [:check, ivar, :foo]
98
- ivar.value(0.1).should eq :foo
99
- end
100
-
101
- it 'returns self' do
102
- expected = subject
103
- result = subject << [1,2,3,4]
104
- result.should eq expected
105
- end
106
- end
107
-
108
- context '#post with callback' do
109
-
110
- specify 'on success calls the callback with time and value' do
111
- expected_value = expected_reason = nil
112
- subject.post(:foo) do |time, value, reason|
113
- expected_value = value
114
- expected_reason = reason
115
- end
116
- sleep(0.1)
117
-
118
- expected_value.should eq :foo
119
- expected_reason.should be_nil
120
- end
121
-
122
- specify 'on failure calls the callback with time and reason' do
123
- expected_value = expected_reason = nil
124
- subject.post(:poison) do |time, value, reason|
125
- expected_value = value
126
- expected_reason = reason
127
- end
128
- sleep(0.1)
129
-
130
- expected_value.should be_nil
131
- expected_reason.should be_a StandardError
132
- end
133
-
134
- it 'supresses exceptions thrown by the callback' do
135
- expected = nil
136
- subject.post(:foo){|time, value, reason| raise StandardError }
137
- sleep(0.1)
138
-
139
- subject.post(:bar){|time, value, reason| expected = value }
140
- sleep(0.1)
141
-
142
- expected.should eq :bar
143
- end
144
- end
145
-
146
- context '#post!' do
147
-
148
- it 'raises an exception when the message is empty' do
149
- expect {
150
- subject.post!(1)
151
- }.to raise_error(ArgumentError)
152
- end
153
-
154
- it 'blocks for up to the given number of seconds' do
155
- start = Time.now.to_f
156
- begin
157
- subject.post!(1, :sleep, 2)
158
- rescue
159
- end
160
- delta = Time.now.to_f - start
161
- delta.should >= 1
162
- delta.should <= 2
163
- end
164
-
165
- it 'blocks forever when the timeout is nil' do
166
- start = Time.now.to_f
167
- subject.post!(nil, :sleep, 1)
168
- delta = Time.now.to_f - start
169
- delta.should > 1
170
- end
171
-
172
- it 'raises a TimeoutError when timeout is zero' do
173
- expect {
174
- subject.post!(0, :foo)
175
- }.to raise_error(Concurrent::TimeoutError)
176
- end
177
-
178
- it 'raises a TimeoutError when the timeout is reached' do
179
- expect {
180
- subject.post!(1, :sleep, 10)
181
- }.to raise_error(Concurrent::TimeoutError)
182
- end
183
-
184
- it 'returns the result of success processing' do
185
- subject.post!(1, :foo).should eq :foo
186
- end
187
-
188
- it 'bubbles exceptions thrown during processing' do
189
- expect {
190
- subject.post!(1, :poison)
191
- }.to raise_error(StandardError)
192
- end
193
- end
194
-
195
- context '#join' do
196
-
197
- it 'blocks until shutdown when no limit is given' do
198
- start = Time.now
199
- subject << :foo # start the actor's thread
200
- Thread.new{ sleep(1); subject.shutdown }
201
- subject.join
202
- stop = Time.now
203
-
204
- subject.should be_shutdown
205
- stop.should >= start + 1
206
- stop.should <= start + 2
207
- end
208
-
209
- it 'blocks for no more than the given number of seconds' do
210
- start = Time.now
211
- subject << :foo # start the actor's thread
212
- Thread.new{ sleep(5); subject.shutdown }
213
- subject.join(1)
214
- stop = Time.now
215
-
216
- stop.should >= start + 1
217
- stop.should <= start + 2
218
- end
219
-
220
- it 'returns true when shutdown has completed before timeout' do
221
- subject << :foo # start the actor's thread
222
- Thread.new{ sleep(1); subject.shutdown }
223
- subject.join.should be_true
224
- end
225
-
226
- it 'returns false on timeout' do
227
- subject << :foo # start the actor's thread
228
- Thread.new{ sleep(5); subject.shutdown }
229
- subject.join(1).should be_false
230
- end
231
-
232
- it 'returns immediately when already shutdown' do
233
- start = Time.now
234
- subject << :foo # start the actor's thread
235
- sleep(0.1)
236
- subject.shutdown
237
- sleep(0.1)
238
-
239
- start = Time.now
240
- subject.join
241
- Time.now.should >= start
242
- Time.now.should <= start + 0.1
243
- end
244
- end
245
-
246
- context '#on_error' do
247
-
248
- specify 'is not called on success' do
249
- actor = subject.instance_variable_get(:@actor)
250
- actor.should_not_receive(:on_error).with(any_args)
251
- subject.post(:foo)
252
- sleep(0.1)
253
- end
254
-
255
- specify 'is called when a message raises an exception' do
256
- actor = subject.instance_variable_get(:@actor)
257
- actor.should_receive(:on_error).
258
- with(anything, [:poison], an_instance_of(StandardError))
259
- subject.post(:poison)
260
- sleep(0.1)
261
- end
262
- end
263
- end