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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG.md +138 -0
  2. data/README.md +108 -95
  3. data/lib/concurrent/actor.rb +12 -13
  4. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +1 -1
  5. data/lib/concurrent/actor/behaviour/executes_context.rb +1 -1
  6. data/lib/concurrent/actor/behaviour/linking.rb +4 -1
  7. data/lib/concurrent/actor/behaviour/pausing.rb +2 -2
  8. data/lib/concurrent/actor/behaviour/supervised.rb +3 -2
  9. data/lib/concurrent/actor/behaviour/terminates_children.rb +1 -1
  10. data/lib/concurrent/actor/behaviour/termination.rb +1 -1
  11. data/lib/concurrent/actor/context.rb +2 -1
  12. data/lib/concurrent/actor/core.rb +8 -4
  13. data/lib/concurrent/actor/utils.rb +10 -0
  14. data/lib/concurrent/actor/utils/ad_hoc.rb +21 -0
  15. data/lib/concurrent/actor/utils/balancer.rb +42 -0
  16. data/lib/concurrent/actor/utils/broadcast.rb +22 -6
  17. data/lib/concurrent/actor/utils/pool.rb +59 -0
  18. data/lib/concurrent/agent.rb +1 -22
  19. data/lib/concurrent/async.rb +1 -79
  20. data/lib/concurrent/atomic.rb +20 -26
  21. data/lib/concurrent/atomic/atomic_boolean.rb +4 -1
  22. data/lib/concurrent/atomic/atomic_fixnum.rb +4 -1
  23. data/lib/concurrent/atomic/thread_local_var.rb +71 -24
  24. data/lib/concurrent/atomic_reference/jruby.rb +10 -6
  25. data/lib/concurrent/atomic_reference/ruby.rb +14 -10
  26. data/lib/concurrent/atomics.rb +0 -1
  27. data/lib/concurrent/configuration.rb +11 -5
  28. data/lib/concurrent/dataflow.rb +1 -30
  29. data/lib/concurrent/dereferenceable.rb +9 -2
  30. data/lib/concurrent/executor/indirect_immediate_executor.rb +46 -0
  31. data/lib/concurrent/executor/java_thread_pool_executor.rb +2 -4
  32. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +24 -22
  33. data/lib/concurrent/executor/serialized_execution.rb +36 -23
  34. data/lib/concurrent/executor/thread_pool_executor.rb +2 -0
  35. data/lib/concurrent/executor/timer_set.rb +7 -8
  36. data/lib/concurrent/executors.rb +1 -0
  37. data/lib/concurrent/future.rb +7 -29
  38. data/lib/concurrent/ivar.rb +9 -0
  39. data/lib/concurrent/logging.rb +3 -0
  40. data/lib/concurrent/mvar.rb +26 -9
  41. data/lib/concurrent/observable.rb +33 -0
  42. data/lib/concurrent/promise.rb +59 -1
  43. data/lib/concurrent/scheduled_task.rb +1 -0
  44. data/lib/concurrent/timer_task.rb +18 -18
  45. data/lib/concurrent/tvar.rb +3 -1
  46. data/lib/concurrent/version.rb +1 -1
  47. data/lib/concurrent_ruby_ext.jar +0 -0
  48. data/lib/concurrent_ruby_ext.so +0 -0
  49. data/lib/extension_helper.rb +25 -6
  50. metadata +15 -7
  51. data/lib/concurrent/actor/ad_hoc.rb +0 -19
  52. data/lib/concurrent/actor/utills.rb +0 -7
@@ -2,8 +2,9 @@ module Concurrent
2
2
  module Actor
3
3
  module Behaviour
4
4
 
5
- # Links the actor to other actors and sends events to them,
5
+ # Links the actor to other actors and sends actor's events to them,
6
6
  # like: `:terminated`, `:paused`, errors, etc
7
+ # TODO example
7
8
  class Linking < Abstract
8
9
  def initialize(core, subsequent)
9
10
  super core, subsequent
@@ -16,6 +17,8 @@ module Concurrent
16
17
  link envelope.sender
17
18
  when :unlink
18
19
  unlink envelope.sender
20
+ when :linked?
21
+ @linked.include? envelope.sender
19
22
  else
20
23
  pass envelope
21
24
  end
@@ -5,8 +5,8 @@ module Concurrent
5
5
  # Allows to pause actors on errors.
6
6
  # When paused all arriving messages are collected and processed after the actor
7
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.
8
+ # Reset also reinitialized context.
9
+ # TODO example
10
10
  class Pausing < Abstract
11
11
  def initialize(core, subsequent)
12
12
  super core, subsequent
@@ -2,8 +2,9 @@ module Concurrent
2
2
  module Actor
3
3
  module Behaviour
4
4
 
5
- # Sets nad holds the supervisor of the actor if any. There is only one or none supervisor
6
- # for each actor. Each supervisor is automatically linked.
5
+ # Sets and holds the supervisor of the actor if any. There is at most one supervisor
6
+ # for each actor. Each supervisor is automatically linked. Messages:
7
+ # `:pause!, :resume!, :reset!, :restart!` are accepted only from supervisor.
7
8
  class Supervised < Abstract
8
9
  attr_reader :supervisor
9
10
 
@@ -4,7 +4,7 @@ module Concurrent
4
4
  # Terminates all children when the actor terminates.
5
5
  class TerminatesChildren < Abstract
6
6
  def on_event(event)
7
- children.each { |ch| ch << :terminate! } if event == :terminated
7
+ children.map { |ch| ch.ask :terminate! }.each(&:wait) if event == :terminated
8
8
  super event
9
9
  end
10
10
  end
@@ -44,7 +44,7 @@ module Concurrent
44
44
  def terminate!
45
45
  return true if terminated?
46
46
  terminated.set
47
- broadcast(:terminated)
47
+ broadcast(:terminated) # TODO do not end up in Dead Letter Router
48
48
  parent << :remove_child if parent
49
49
  true
50
50
  end
@@ -115,7 +115,8 @@ module Concurrent
115
115
  undef_method :spawn
116
116
  end
117
117
 
118
- # Basic Context of an Actor.
118
+ # Basic Context of an Actor. It does not support supervision and pausing.
119
+ # It simply terminates on error.
119
120
  #
120
121
  # - linking
121
122
  # - terminates on error
@@ -37,7 +37,7 @@ module Concurrent
37
37
  # @option opts [true, false] link, atomically link the actor to its parent
38
38
  # @option opts [true, false] supervise, atomically supervise the actor by its parent
39
39
  # @option opts [Array<Array(Behavior::Abstract, Array<Object>)>] behaviour_definition, array of pairs
40
- # where each pair is behaviour class and its args, see {Behaviour.basic_behaviour}
40
+ # where each pair is behaviour class and its args, see {Behaviour.basic_behaviour_definition}
41
41
  # @option opts [IVar, nil] initialized, if present it'll be set or failed after {Context} initialization
42
42
  # @option opts [Proc, nil] logger a proc accepting (level, progname, message = nil, &block) params,
43
43
  # can be used to hook actor instance to any logging system
@@ -46,10 +46,14 @@ module Concurrent
46
46
  synchronize do
47
47
  @mailbox = Array.new
48
48
  @serialized_execution = SerializedExecution.new
49
- @executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
50
49
  @children = Set.new
51
- @context_class = Child! opts.fetch(:class), AbstractContext
50
+
51
+ @context_class = Child! opts.fetch(:class), AbstractContext
52
52
  allocate_context
53
+
54
+ @executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
55
+ raise ArgumentError, 'ImmediateExecutor is not supported' if @executor.is_a? ImmediateExecutor
56
+
53
57
  @reference = (Child! opts[:reference_class] || @context.default_reference_class, Reference).new self
54
58
  @name = (Type! opts.fetch(:name), String, Symbol).to_s
55
59
 
@@ -82,7 +86,7 @@ module Concurrent
82
86
  handle_envelope Envelope.new(message, nil, parent, reference)
83
87
  end
84
88
 
85
- initialized.set true if initialized
89
+ initialized.set reference if initialized
86
90
  rescue => ex
87
91
  log ERROR, ex
88
92
  @first_behaviour.terminate!
@@ -0,0 +1,10 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Utils
4
+ require 'concurrent/actor/utils/ad_hoc'
5
+ require 'concurrent/actor/utils/broadcast'
6
+ require 'concurrent/actor/utils/balancer'
7
+ require 'concurrent/actor/utils/pool'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Utils
4
+ # Allows quick creation of actors with behaviour defined by blocks.
5
+ # @example ping
6
+ # AdHoc.spawn :forward, an_actor do |where|
7
+ # # this block has to return proc defining #on_message behaviour
8
+ # -> message { where.tell message }
9
+ # end
10
+ class AdHoc < Context
11
+ def initialize(*args, &initializer)
12
+ @on_message = Type! initializer.call(*args), Proc
13
+ end
14
+
15
+ def on_message(message)
16
+ instance_exec message, &@on_message
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ module Concurrent
2
+ module Actor
3
+ module Utils
4
+
5
+ # Distributes messages between subscribed actors. Each actor'll get only one message then
6
+ # it's unsubscribed. The actor needs to resubscribe when it's ready to receive next message.
7
+ # It will buffer the messages if there is no worker registered.
8
+ # @see Pool
9
+ class Balancer < RestartingContext
10
+
11
+ def initialize
12
+ @receivers = []
13
+ @buffer = []
14
+ end
15
+
16
+ def on_message(message)
17
+ case message
18
+ when :subscribe
19
+ @receivers << envelope.sender
20
+ distribute
21
+ true
22
+ when :unsubscribe
23
+ @receivers.delete envelope.sender
24
+ true
25
+ when :subscribed?
26
+ @receivers.include? envelope.sender
27
+ else
28
+ @buffer << envelope
29
+ distribute
30
+ Behaviour::MESSAGE_PROCESSED
31
+ end
32
+ end
33
+
34
+ def distribute
35
+ while !@receivers.empty? && !@buffer.empty?
36
+ redirect @receivers.shift, @buffer.shift
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -4,8 +4,20 @@ module Concurrent
4
4
  module Actor
5
5
  module Utils
6
6
 
7
- # TODO doc
8
- class Broadcast < Context
7
+ # Allows to build pub/sub easily.
8
+ # @example news
9
+ # news_channel = Concurrent::Actor::Utils::Broadcast.spawn :news
10
+ #
11
+ # 2.times do |i|
12
+ # Concurrent::Actor::Utils::AdHoc.spawn "listener-#{i}" do
13
+ # news_channel << :subscribe
14
+ # -> message { puts message }
15
+ # end
16
+ # end
17
+ #
18
+ # news_channel << 'Ruby rocks!'
19
+ # # prints: 'Ruby rocks!' twice
20
+ class Broadcast < RestartingContext
9
21
 
10
22
  def initialize
11
23
  @receivers = Set.new
@@ -14,11 +26,14 @@ module Concurrent
14
26
  def on_message(message)
15
27
  case message
16
28
  when :subscribe
17
- @receivers.add envelope.sender
18
- true
29
+ if envelope.sender.is_a? Reference
30
+ @receivers.add envelope.sender
31
+ true
32
+ else
33
+ false
34
+ end
19
35
  when :unsubscribe
20
- @receivers.delete envelope.sender
21
- true
36
+ !!@receivers.delete(envelope.sender)
22
37
  when :subscribed?
23
38
  @receivers.include? envelope.sender
24
39
  else
@@ -31,6 +46,7 @@ module Concurrent
31
46
  @receivers
32
47
  end
33
48
  end
49
+
34
50
  end
35
51
  end
36
52
  end
@@ -0,0 +1,59 @@
1
+ require 'concurrent/actor/utils/balancer'
2
+
3
+ module Concurrent
4
+ module Actor
5
+ module Utils
6
+
7
+ # Allows to create a pool of workers and distribute work between them
8
+ # @param [Integer] size number of workers
9
+ # @yield [balancer, index] a block spawning an worker instance. called +size+ times.
10
+ # The worker should be descendant of AbstractWorker and supervised, see example.
11
+ # @yieldparam [Balancer] balancer to pass to the worker
12
+ # @yieldparam [Integer] index of the worker, usually used in its name
13
+ # @yieldreturn [Reference] the reference of newly created worker
14
+ # @example
15
+ # class Worker < Concurrent::Actor::Utils::AbstractWorker
16
+ # def work(message)
17
+ # p message * 5
18
+ # end
19
+ # end
20
+ #
21
+ # pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |balancer, index|
22
+ # Worker.spawn name: "worker-#{index}", supervise: true, args: [balancer]
23
+ # end
24
+ #
25
+ # pool << 'asd' << 2
26
+ # # prints:
27
+ # # "asdasdasdasdasd"
28
+ # # 10
29
+ class Pool < RestartingContext
30
+ def initialize(size, &worker_initializer)
31
+ @balancer = Balancer.spawn name: :balancer, supervise: true
32
+ @workers = Array.new(size, &worker_initializer.curry[@balancer])
33
+ @workers.each { |w| Type! w, Reference }
34
+ end
35
+
36
+ def on_message(message)
37
+ redirect @balancer
38
+ end
39
+ end
40
+
41
+ class AbstractWorker < RestartingContext
42
+ def initialize(balancer)
43
+ @balancer = balancer
44
+ @balancer << :subscribe
45
+ end
46
+
47
+ def on_message(message)
48
+ work message
49
+ ensure
50
+ @balancer << :subscribe
51
+ end
52
+
53
+ def work(message)
54
+ raise NotImplementedError
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -8,28 +8,7 @@ require 'concurrent/logging'
8
8
 
9
9
  module Concurrent
10
10
 
11
- # An agent is a single atomic value that represents an identity. The current value
12
- # of the agent can be requested at any time (`#deref`). Each agent has a work queue and operates on
13
- # the global thread pool. Consumers can `#post` code blocks to the agent. The code block (function)
14
- # will receive the current value of the agent as its sole parameter. The return value of the block
15
- # will become the new value of the agent. Agents support two error handling modes: fail and continue.
16
- # A good example of an agent is a shared incrementing counter, such as the score in a video game.
17
- #
18
- # @example Basic usage
19
- # score = Concurrent::Agent.new(10)
20
- # score.value #=> 10
21
- #
22
- # score << proc{|current| current + 100 }
23
- # sleep(0.1)
24
- # score.value #=> 110
25
- #
26
- # score << proc{|current| current * 2 }
27
- # sleep(0.1)
28
- # score.value #=> 220
29
- #
30
- # score << proc{|current| current - 50 }
31
- # sleep(0.1)
32
- # score.value #=> 170
11
+ # {include:file:doc/agent.md}
33
12
  #
34
13
  # @!attribute [r] timeout
35
14
  # @return [Fixnum] the maximum number of seconds before an update is cancelled
@@ -8,85 +8,7 @@ require 'concurrent/executor/serialized_execution'
8
8
 
9
9
  module Concurrent
10
10
 
11
- # A mixin module that provides simple asynchronous behavior to any standard
12
- # class/object or object.
13
- #
14
- # Scenario:
15
- # As a stateful, plain old Ruby class/object
16
- # I want safe, asynchronous behavior
17
- # So my long-running methods don't block the main thread
18
- #
19
- # Stateful, mutable objects must be managed carefully when used asynchronously.
20
- # But Ruby is an object-oriented language so designing with objects and classes
21
- # plays to Ruby's strengths and is often more natural to many Ruby programmers.
22
- # The `Async` module is a way to mix simple yet powerful asynchronous capabilities
23
- # into any plain old Ruby object or class. These capabilities provide a reasonable
24
- # level of thread safe guarantees when used correctly.
25
- #
26
- # When this module is mixed into a class or object it provides to new methods:
27
- # `async` and `await`. These methods are thread safe with respect to the enclosing
28
- # object. The former method allows methods to be called asynchronously by posting
29
- # to the global thread pool. The latter allows a method to be called synchronously
30
- # on the current thread but does so safely with respect to any pending asynchronous
31
- # method calls. Both methods return an `Obligation` which can be inspected for
32
- # the result of the method call. Calling a method with `async` will return a
33
- # `:pending` `Obligation` whereas `await` will return a `:complete` `Obligation`.
34
- #
35
- # Very loosely based on the `async` and `await` keywords in C#.
36
- #
37
- # @example Defining an asynchronous class
38
- # class Echo
39
- # include Concurrent::Async
40
- #
41
- # def initialize
42
- # init_mutex # initialize the internal synchronization objects
43
- # end
44
- #
45
- # def echo(msg)
46
- # sleep(rand)
47
- # print "#{msg}\n"
48
- # nil
49
- # end
50
- # end
51
- #
52
- # horn = Echo.new
53
- # horn.echo('zero') # synchronous, not thread-safe
54
- #
55
- # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
56
- # horn.await.echo('two') # synchronous, blocking, thread-safe
57
- #
58
- # @example Monkey-patching an existing object
59
- # numbers = 1_000_000.times.collect{ rand }
60
- # numbers.extend(Concurrent::Async)
61
- # numbers.init_mutex # initialize the internal synchronization objects
62
- #
63
- # future = numbers.async.max
64
- # future.state #=> :pending
65
- #
66
- # sleep(2)
67
- #
68
- # future.state #=> :fulfilled
69
- # future.value #=> 0.999999138918843
70
- #
71
- # @note This module depends on several internal synchronization objects that
72
- # must be initialized prior to calling any of the async/await/executor methods.
73
- # The best practice is to call `init_mutex` from within the constructor
74
- # of the including class. A less ideal but acceptable practice is for the
75
- # thread creating the asynchronous object to explicitly call the `init_mutex`
76
- # method prior to calling any of the async/await/executor methods. If
77
- # `init_mutex` is *not* called explicitly the async/await/executor methods
78
- # will raize a `Concurrent::InitializationError`. This is the only way
79
- # thread-safe initialization can be guaranteed.
80
- #
81
- # @note Thread safe guarantees can only be made when asynchronous method calls
82
- # are not mixed with synchronous method calls. Use only synchronous calls
83
- # when the object is used exclusively on a single thread. Use only
84
- # `async` and `await` when the object is shared between threads. Once you
85
- # call a method using `async`, you should no longer call any methods
86
- # directly on the object. Use `async` and `await` exclusively from then on.
87
- # With careful programming it is possible to switch back and forth but it's
88
- # also very easy to create race conditions and break your application.
89
- # Basically, it's "async all the way down."
11
+ # {include:file:doc/async.md}
90
12
  #
91
13
  # @since 0.6.0
92
14
  #
@@ -1,3 +1,17 @@
1
+ #####################################################################
2
+ # Attempt to check for the deprecated ruby-atomic gem and warn the
3
+ # user that they should use the new implementation instead.
4
+
5
+ if defined?(Atomic)
6
+ warn <<-RUBY
7
+ [ATOMIC] Detected an `Atomic` class, which may indicate a dependency
8
+ on the ruby-atomic gem. That gem has been deprecated and merged into
9
+ the concurrent-ruby gem. Please use the Concurrent::Atomic class for
10
+ atomic references and not the Atomic class.
11
+ RUBY
12
+ end
13
+ #####################################################################
14
+
1
15
  require 'concurrent/atomic_reference/concurrent_update_error'
2
16
  require 'concurrent/atomic_reference/mutex_atomic'
3
17
 
@@ -11,7 +25,7 @@ begin
11
25
 
12
26
  require "concurrent/atomic_reference/#{ruby_engine}"
13
27
  rescue LoadError
14
- warn 'Compiled extensions not installed, pure Ruby Atomic will be used.'
28
+ #warn 'Compiled extensions not installed, pure Ruby Atomic will be used.'
15
29
  end
16
30
 
17
31
  if defined? Concurrent::JavaAtomic
@@ -57,41 +71,21 @@ if defined? Concurrent::JavaAtomic
57
71
  class Concurrent::Atomic < Concurrent::JavaAtomic
58
72
  end
59
73
 
60
- elsif defined? Concurrent::CAtomic
61
-
62
- # @!macro [attach] concurrent_update_error
63
- #
64
- # This exception may be thrown by methods that have detected concurrent
65
- # modification of an object when such modification is not permissible.
66
- class Concurrent::Atomic < Concurrent::CAtomic
67
- end
68
-
69
74
  elsif defined? Concurrent::RbxAtomic
70
75
 
71
76
  # @!macro atomic_reference
72
77
  class Concurrent::Atomic < Concurrent::RbxAtomic
73
78
  end
74
79
 
75
- else
80
+ elsif Concurrent.allow_c_native_class?('CAtomic')
76
81
 
77
82
  # @!macro atomic_reference
78
- class Concurrent::Atomic < Concurrent::MutexAtomic
83
+ class Concurrent::Atomic < Concurrent::CAtomic
79
84
  end
80
- end
81
85
 
82
- # @!macro atomic_reference
83
- class Atomic < Concurrent::Atomic
84
-
85
- # @!macro concurrent_update_error
86
- ConcurrentUpdateError = Class.new(Concurrent::ConcurrentUpdateError)
86
+ else
87
87
 
88
- # @!macro [attach] atomic_reference_method_initialize
89
- #
90
- # Creates a new Atomic reference with null initial value.
91
- #
92
- # @param [Object] value the initial value
93
- def initialize(value)
94
- warn "[DEPRECATED] Please use Concurrent::Atomic instead."
95
- super
88
+ # @!macro atomic_reference
89
+ class Concurrent::Atomic < Concurrent::MutexAtomic
96
90
  end
97
91
  end