concurrent-ruby 0.7.0.rc1-java → 0.7.0.rc2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/README.md +3 -2
  2. data/lib/concurrent.rb +2 -1
  3. data/lib/concurrent/actor.rb +104 -0
  4. data/lib/concurrent/{actress → actor}/ad_hoc.rb +2 -3
  5. data/lib/concurrent/actor/behaviour.rb +70 -0
  6. data/lib/concurrent/actor/behaviour/abstract.rb +48 -0
  7. data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
  8. data/lib/concurrent/actor/behaviour/buffer.rb +54 -0
  9. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
  10. data/lib/concurrent/actor/behaviour/executes_context.rb +18 -0
  11. data/lib/concurrent/actor/behaviour/linking.rb +42 -0
  12. data/lib/concurrent/actor/behaviour/pausing.rb +77 -0
  13. data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
  14. data/lib/concurrent/actor/behaviour/sets_results.rb +36 -0
  15. data/lib/concurrent/actor/behaviour/supervised.rb +58 -0
  16. data/lib/concurrent/actor/behaviour/supervising.rb +34 -0
  17. data/lib/concurrent/actor/behaviour/terminates_children.rb +13 -0
  18. data/lib/concurrent/actor/behaviour/termination.rb +54 -0
  19. data/lib/concurrent/actor/context.rb +153 -0
  20. data/lib/concurrent/actor/core.rb +213 -0
  21. data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
  22. data/lib/concurrent/{actress → actor}/envelope.rb +1 -1
  23. data/lib/concurrent/actor/errors.rb +27 -0
  24. data/lib/concurrent/actor/internal_delegations.rb +49 -0
  25. data/lib/concurrent/{actress/core_delegations.rb → actor/public_delegations.rb} +11 -13
  26. data/lib/concurrent/{actress → actor}/reference.rb +25 -8
  27. data/lib/concurrent/actor/root.rb +37 -0
  28. data/lib/concurrent/{actress → actor}/type_check.rb +1 -1
  29. data/lib/concurrent/actor/utills.rb +7 -0
  30. data/lib/concurrent/actor/utils/broadcast.rb +36 -0
  31. data/lib/concurrent/actress.rb +2 -224
  32. data/lib/concurrent/agent.rb +10 -12
  33. data/lib/concurrent/atomic.rb +32 -1
  34. data/lib/concurrent/atomic/atomic_boolean.rb +55 -13
  35. data/lib/concurrent/atomic/atomic_fixnum.rb +54 -16
  36. data/lib/concurrent/atomic/synchronization.rb +51 -0
  37. data/lib/concurrent/atomic/thread_local_var.rb +15 -50
  38. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  39. data/lib/concurrent/atomic_reference/ruby.rb +15 -0
  40. data/lib/concurrent/atomics.rb +1 -0
  41. data/lib/concurrent/channel/unbuffered_channel.rb +2 -1
  42. data/lib/concurrent/configuration.rb +6 -3
  43. data/lib/concurrent/dataflow.rb +20 -3
  44. data/lib/concurrent/delay.rb +23 -31
  45. data/lib/concurrent/executor/executor.rb +7 -2
  46. data/lib/concurrent/executor/timer_set.rb +1 -1
  47. data/lib/concurrent/future.rb +2 -1
  48. data/lib/concurrent/lazy_register.rb +58 -0
  49. data/lib/concurrent/options_parser.rb +4 -2
  50. data/lib/concurrent/promise.rb +2 -1
  51. data/lib/concurrent/scheduled_task.rb +6 -5
  52. data/lib/concurrent/tvar.rb +6 -10
  53. data/lib/concurrent/utility/processor_count.rb +4 -2
  54. data/lib/concurrent/version.rb +1 -1
  55. data/lib/concurrent_ruby_ext.jar +0 -0
  56. data/lib/concurrent_ruby_ext.so +0 -0
  57. metadata +32 -11
  58. data/lib/concurrent/actress/context.rb +0 -98
  59. data/lib/concurrent/actress/core.rb +0 -228
  60. data/lib/concurrent/actress/errors.rb +0 -14
  61. data/lib/concurrent_ruby_ext.bundle +0 -0
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Concurrent Ruby
2
- [![Gem Version](https://badge.fury.io/rb/concurrent-ruby.png)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Coverage Status](https://coveralls.io/repos/ruby-concurrency/concurrent-ruby/badge.png)](https://coveralls.io/r/ruby-concurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.png)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.png)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.png)](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [![Gitter chat](https://badges.gitter.im/ruby-concurrency.png)](https://gitter.im/ruby-concurrency)
2
+ [![Gem Version](https://badge.fury.io/rb/concurrent-ruby.png)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Coverage Status](https://coveralls.io/repos/ruby-concurrency/concurrent-ruby/badge.png)](https://coveralls.io/r/ruby-concurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.png)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.png)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.png)](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [![Gitter chat](https://badges.gitter.im/ruby-concurrency/concurrent-ruby.png)](https://gitter.im/ruby-concurrency/concurrent-ruby)
3
3
 
4
4
  <table>
5
5
  <tr>
@@ -54,7 +54,7 @@ _NOTE: There is an old gem from 2007 called "concurrent" that does not appear to
54
54
  ## Features & Documentation
55
55
 
56
56
  Please see the [Concurrent Ruby Wiki](https://github.com/ruby-concurrency/concurrent-ruby/wiki)
57
- or the [API documentation](http://ruby-concurrency.github.io/concurrent-ruby/frames.html))
57
+ or the [API documentation](http://ruby-concurrency.github.io/concurrent-ruby/frames.html)
58
58
  for more information or join our [mailing list](http://groups.google.com/group/concurrent-ruby).
59
59
 
60
60
  There are many concurrency abstractions in this library. These abstractions can be broadly categorized
@@ -146,6 +146,7 @@ task.value #=> 25.96
146
146
  * [Chip Miller](https://github.com/chip-miller)
147
147
  * [Giuseppe Capizzi](https://github.com/gcapizzi)
148
148
  * [Jamie Hodge](https://github.com/jamiehodge)
149
+ * [Justin Lambert](https://github.com/mastfish)
149
150
  * [Larry Lv](https://github.com/larrylv)
150
151
  * [Maxim Chechel](https://github.com/maximchick)
151
152
  * [Ravil Bayramgalin](https://github.com/brainopia)
data/lib/concurrent.rb CHANGED
@@ -8,8 +8,9 @@ require 'concurrent/collections'
8
8
  require 'concurrent/executors'
9
9
  require 'concurrent/utilities'
10
10
 
11
- require 'concurrent/actress'
11
+ require 'concurrent/actor'
12
12
  require 'concurrent/atomic'
13
+ require 'concurrent/lazy_register'
13
14
  require 'concurrent/agent'
14
15
  require 'concurrent/async'
15
16
  require 'concurrent/dataflow'
@@ -0,0 +1,104 @@
1
+ require 'concurrent/configuration'
2
+ require 'concurrent/executor/serialized_execution'
3
+ require 'concurrent/ivar'
4
+ require 'concurrent/logging'
5
+ require 'concurrent/atomic/synchronization'
6
+
7
+ module Concurrent
8
+ # TODO https://github.com/celluloid/celluloid/wiki/Supervision-Groups
9
+
10
+ # TODO doc
11
+ # - what happens if I try to supervise using a normal Context?
12
+
13
+
14
+ # {include:file:doc/actor/main.md}
15
+ module Actor
16
+
17
+ require 'concurrent/actor/type_check'
18
+ require 'concurrent/actor/errors'
19
+ require 'concurrent/actor/public_delegations'
20
+ require 'concurrent/actor/internal_delegations'
21
+ require 'concurrent/actor/envelope'
22
+ require 'concurrent/actor/reference'
23
+ require 'concurrent/actor/core'
24
+ require 'concurrent/actor/behaviour'
25
+ require 'concurrent/actor/context'
26
+
27
+ require 'concurrent/actor/default_dead_letter_handler'
28
+ require 'concurrent/actor/root'
29
+ require 'concurrent/actor/ad_hoc'
30
+
31
+ # @return [Reference, nil] current executing actor if any
32
+ def self.current
33
+ Thread.current[:__current_actor__]
34
+ end
35
+
36
+ @root = Delay.new do
37
+ Core.new(parent: nil, name: '/', class: Root, initialized: ivar = IVar.new).reference.tap do
38
+ ivar.no_error!
39
+ end
40
+ end
41
+
42
+ # A root actor, a default parent of all actors spawned outside an actor
43
+ def self.root
44
+ @root.value!
45
+ end
46
+
47
+ # Spawns a new actor.
48
+ #
49
+ # @example simple
50
+ # Actor.spawn(AdHoc, :ping1) { -> message { message } }
51
+ #
52
+ # @example complex
53
+ # Actor.spawn name: :ping3,
54
+ # class: AdHoc,
55
+ # args: [1]
56
+ # executor: Concurrent.configuration.global_task_pool do |add|
57
+ # lambda { |number| number + add }
58
+ # end
59
+ #
60
+ # @param block for context_class instantiation
61
+ # @param args see {.spawn_optionify}
62
+ # @return [Reference] never the actual actor
63
+ def self.spawn(*args, &block)
64
+ experimental_acknowledged? or
65
+ warn '[EXPERIMENTAL] A full release of `Actor`, is expected in the 0.7.0 release.'
66
+
67
+ if Actor.current
68
+ Core.new(spawn_optionify(*args).merge(parent: Actor.current), &block).reference
69
+ else
70
+ root.ask([:spawn, spawn_optionify(*args), block]).value
71
+ end
72
+ end
73
+
74
+ # as {.spawn} but it'll raise when Actor not initialized properly
75
+ def self.spawn!(*args, &block)
76
+ spawn(spawn_optionify(*args).merge(initialized: ivar = IVar.new), &block).tap { ivar.no_error! }
77
+ end
78
+
79
+ # @overload spawn_optionify(context_class, name, *args)
80
+ # @param [Context] context_class to be spawned
81
+ # @param [String, Symbol] name of the instance, it's used to generate the {Core#path} of the actor
82
+ # @param args for context_class instantiation
83
+ # @overload spawn_optionify(opts)
84
+ # see {Core#initialize} opts
85
+ def self.spawn_optionify(*args)
86
+ if args.size == 1 && args.first.is_a?(Hash)
87
+ args.first
88
+ else
89
+ { class: args[0],
90
+ name: args[1],
91
+ args: args[2..-1] }
92
+ end
93
+ end
94
+
95
+ # call this to disable experimental warning
96
+ def self.i_know_it_is_experimental!
97
+ @experimental_acknowledged = true
98
+ end
99
+
100
+ def self.experimental_acknowledged?
101
+ !!@experimental_acknowledged
102
+ end
103
+ end
104
+ end
@@ -1,13 +1,12 @@
1
1
  module Concurrent
2
- module Actress
2
+ module Actor
3
3
  # Allows quick creation of actors with behaviour defined by blocks.
4
4
  # @example ping
5
5
  # AdHoc.spawn :forward, an_actor do |where|
6
6
  # # this block has to return proc defining #on_message behaviour
7
7
  # -> message { where.tell message }
8
8
  # end
9
- class AdHoc
10
- include Context
9
+ class AdHoc < Context
11
10
  def initialize(*args, &initializer)
12
11
  @on_message = Type! initializer.call(*args), Proc
13
12
  end
@@ -0,0 +1,70 @@
1
+ module Concurrent
2
+ module Actor
3
+
4
+ # Actors have modular architecture, which is achieved by combining a light core with chain of
5
+ # behaviours. Each message or internal event propagates through the chain allowing the
6
+ # behaviours react based on their responsibility. listing few as an example:
7
+ #
8
+ # - {Behaviour::Linking}:
9
+ #
10
+ # > {include:Actor::Behaviour::Linking}
11
+ #
12
+ # - {Behaviour::Awaits}:
13
+ #
14
+ # > {include:Actor::Behaviour::Awaits}
15
+ #
16
+ # See {Behaviour}'s namespace fo other behaviours.
17
+ # If needed new behaviours can be added, or old one removed to get required behaviour.
18
+ module Behaviour
19
+ MESSAGE_PROCESSED = Object.new
20
+
21
+ require 'concurrent/actor/behaviour/abstract'
22
+ require 'concurrent/actor/behaviour/awaits'
23
+ require 'concurrent/actor/behaviour/buffer'
24
+ require 'concurrent/actor/behaviour/errors_on_unknown_message'
25
+ require 'concurrent/actor/behaviour/executes_context'
26
+ require 'concurrent/actor/behaviour/linking'
27
+ require 'concurrent/actor/behaviour/pausing'
28
+ require 'concurrent/actor/behaviour/removes_child'
29
+ require 'concurrent/actor/behaviour/sets_results'
30
+ require 'concurrent/actor/behaviour/supervised'
31
+ require 'concurrent/actor/behaviour/supervising'
32
+ require 'concurrent/actor/behaviour/termination'
33
+ require 'concurrent/actor/behaviour/terminates_children'
34
+
35
+ def self.basic_behaviour_definition
36
+ [*base,
37
+ *user_messages(:terminate!)]
38
+ end
39
+
40
+ def self.restarting_behaviour_definition
41
+ [*base,
42
+ *supervised,
43
+ [Behaviour::Supervising, [:reset!, :one_for_one]],
44
+ *user_messages(:pause!)]
45
+ end
46
+
47
+ def self.base
48
+ [[SetResults, [:terminate!]],
49
+ # has to be before Termination to be able to remove children form terminated actor
50
+ [RemovesChild, []],
51
+ [Termination, []],
52
+ [TerminatesChildren, []],
53
+ [Linking, []]]
54
+ end
55
+
56
+ def self.supervised
57
+ [[Supervised, []],
58
+ [Pausing, []]]
59
+ end
60
+
61
+ def self.user_messages(on_error)
62
+ [[Buffer, []],
63
+ [SetResults, [on_error]],
64
+ [Awaits, []],
65
+ [ExecutesContext, []],
66
+ [ErrorsOnUnknownMessage, []]]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,48 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+ class Abstract
5
+ include TypeCheck
6
+ include InternalDelegations
7
+
8
+ attr_reader :core, :subsequent
9
+
10
+ def initialize(core, subsequent)
11
+ @core = Type! core, Core
12
+ @subsequent = Type! subsequent, Abstract, NilClass
13
+ end
14
+
15
+ # override to add extra behaviour
16
+ # @note super needs to be called not to break the chain
17
+ def on_envelope(envelope)
18
+ pass envelope
19
+ end
20
+
21
+ # @param [Envelope] envelope to pass to {#subsequent} behaviour
22
+ def pass(envelope)
23
+ subsequent.on_envelope envelope
24
+ end
25
+
26
+ # override to add extra behaviour
27
+ # @note super needs to be called not to break the chain
28
+ def on_event(event)
29
+ subsequent.on_event event if subsequent
30
+ end
31
+
32
+ # broadcasts event to all behaviours and context
33
+ # @see #on_event
34
+ # @see AbstractContext#on_event
35
+ def broadcast(event)
36
+ core.broadcast(event)
37
+ end
38
+
39
+ def reject_envelope(envelope)
40
+ envelope.reject! ActorTerminated.new(reference)
41
+ dead_letter_routing << envelope unless envelope.ivar
42
+ log Logging::DEBUG, "rejected #{envelope.message} from #{envelope.sender_path}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,21 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+
5
+ # Handles `:await` messages. Which allows to wait on Actor to process all previously send
6
+ # messages.
7
+ # @example
8
+ # actor << :a << :b
9
+ # actor.ask(:await).wait # blocks until :a and :b are processed
10
+ class Awaits < Abstract
11
+ def on_envelope(envelope)
12
+ if envelope.message == :await
13
+ true
14
+ else
15
+ pass envelope
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+
5
+ # Any message reaching this behaviour is buffered. Only one message is is scheduled
6
+ # at any given time. Others are kept in buffer until another one can be scheduled.
7
+ # This effective means that messages handled by behaviours before buffer have higher priority
8
+ # and they can be processed before messages arriving into buffer. This allows to
9
+ # process internal actor messages like (`:link`, `:supervise`) processed first.
10
+ class Buffer < Abstract
11
+ def initialize(core, subsequent)
12
+ super core, subsequent
13
+ @buffer = []
14
+ @receive_envelope_scheduled = false
15
+ end
16
+
17
+ def on_envelope(envelope)
18
+ @buffer.push envelope
19
+ process_envelopes?
20
+ MESSAGE_PROCESSED
21
+ end
22
+
23
+ # Ensures that only one envelope processing is scheduled with #schedule_execution,
24
+ # this allows other scheduled blocks to be executed before next envelope processing.
25
+ # Simply put this ensures that Core is still responsive to internal calls (like add_child)
26
+ # even though the Actor is flooded with messages.
27
+ def process_envelopes?
28
+ unless @buffer.empty? || @receive_envelope_scheduled
29
+ @receive_envelope_scheduled = true
30
+ process_envelope
31
+ end
32
+ end
33
+
34
+ def process_envelope
35
+ envelope = @buffer.shift
36
+ return nil unless envelope
37
+ pass envelope
38
+ ensure
39
+ @receive_envelope_scheduled = false
40
+ core.schedule_execution { process_envelopes? }
41
+ end
42
+
43
+ def on_event(event)
44
+ case event
45
+ when :terminated, :restarted
46
+ @buffer.each { |envelope| reject_envelope envelope }
47
+ @buffer.clear
48
+ end
49
+ super event
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,12 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+ # Simply fails when message arrives here.
5
+ class ErrorsOnUnknownMessage < Abstract
6
+ def on_envelope(envelope)
7
+ raise UnknownMessage, envelope
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+ # Delegates messages nad events to {AbstractContext} instance
5
+ class ExecutesContext < Abstract
6
+ def on_envelope(envelope)
7
+ context.on_envelope envelope
8
+ end
9
+
10
+ def on_event(event)
11
+ context.on_event(event)
12
+ core.log Logging::DEBUG, "event: #{event.inspect}"
13
+ super event
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,42 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+
5
+ # Links the actor to other actors and sends events to them,
6
+ # like: `:terminated`, `:paused`, errors, etc
7
+ class Linking < Abstract
8
+ def initialize(core, subsequent)
9
+ super core, subsequent
10
+ @linked = Set.new
11
+ end
12
+
13
+ def on_envelope(envelope)
14
+ case envelope.message
15
+ when :link
16
+ link envelope.sender
17
+ when :unlink
18
+ unlink envelope.sender
19
+ else
20
+ pass envelope
21
+ end
22
+ end
23
+
24
+ def link(ref)
25
+ @linked.add(ref)
26
+ true
27
+ end
28
+
29
+ def unlink(ref)
30
+ @linked.delete(ref)
31
+ true
32
+ end
33
+
34
+ def on_event(event)
35
+ @linked.each { |a| a << event }
36
+ @linked.clear if event == :terminated
37
+ super event
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,77 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Behaviour
4
+
5
+ # Allows to pause actors on errors.
6
+ # When paused all arriving messages are collected and processed after the actor
7
+ # is resumed or reset. Resume will simply continue with next message.
8
+ # Reset also reinitialized context. `:reset!` and `:resume!` messages are only accepted
9
+ # form supervisor, see Supervised behaviour.
10
+ class Pausing < Abstract
11
+ def initialize(core, subsequent)
12
+ super core, subsequent
13
+ @paused = false
14
+ @buffer = []
15
+ end
16
+
17
+ def on_envelope(envelope)
18
+ case envelope.message
19
+ when :pause!
20
+ pause!
21
+ when :resume!
22
+ resume!
23
+ when :reset!
24
+ reset!
25
+ when :restart!
26
+ restart!
27
+ else
28
+ if @paused
29
+ @buffer << envelope
30
+ MESSAGE_PROCESSED
31
+ else
32
+ pass envelope
33
+ end
34
+ end
35
+ end
36
+
37
+ def pause!(error = nil)
38
+ @paused = true
39
+ broadcast(error || :paused)
40
+ true
41
+ end
42
+
43
+ def resume!(broadcast = true)
44
+ @paused = false
45
+ broadcast(:resumed) if broadcast
46
+ true
47
+ end
48
+
49
+ def reset!(broadcast = true)
50
+ core.allocate_context
51
+ core.build_context
52
+ resume!(false)
53
+ broadcast(:reset) if broadcast
54
+ true
55
+ end
56
+
57
+ def restart!
58
+ reset! false
59
+ broadcast(:restarted)
60
+ true
61
+ end
62
+
63
+ def on_event(event)
64
+ case event
65
+ when :terminated, :restarted
66
+ @buffer.each { |envelope| reject_envelope envelope }
67
+ @buffer.clear
68
+ when :resumed, :reset
69
+ @buffer.each { |envelope| core.schedule_execution { pass envelope } }
70
+ @buffer.clear
71
+ end
72
+ super event
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end