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

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 (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