concurrent-ruby 0.7.0.rc0-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +166 -0
  4. data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
  5. data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
  6. data/ext/concurrent_ruby_ext/extconf.rb +59 -0
  7. data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
  8. data/lib/concurrent.rb +45 -0
  9. data/lib/concurrent/actress.rb +221 -0
  10. data/lib/concurrent/actress/ad_hoc.rb +20 -0
  11. data/lib/concurrent/actress/context.rb +98 -0
  12. data/lib/concurrent/actress/core.rb +228 -0
  13. data/lib/concurrent/actress/core_delegations.rb +42 -0
  14. data/lib/concurrent/actress/envelope.rb +41 -0
  15. data/lib/concurrent/actress/errors.rb +14 -0
  16. data/lib/concurrent/actress/reference.rb +64 -0
  17. data/lib/concurrent/actress/type_check.rb +48 -0
  18. data/lib/concurrent/agent.rb +232 -0
  19. data/lib/concurrent/async.rb +319 -0
  20. data/lib/concurrent/atomic.rb +46 -0
  21. data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
  22. data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
  23. data/lib/concurrent/atomic/condition.rb +67 -0
  24. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
  25. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
  26. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  27. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  28. data/lib/concurrent/atomic/event.rb +98 -0
  29. data/lib/concurrent/atomic/thread_local_var.rb +117 -0
  30. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  31. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  32. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  33. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  34. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  35. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  36. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  37. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  38. data/lib/concurrent/atomics.rb +10 -0
  39. data/lib/concurrent/channel/buffered_channel.rb +85 -0
  40. data/lib/concurrent/channel/channel.rb +41 -0
  41. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  42. data/lib/concurrent/channel/waitable_list.rb +40 -0
  43. data/lib/concurrent/channels.rb +5 -0
  44. data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
  45. data/lib/concurrent/collection/priority_queue.rb +305 -0
  46. data/lib/concurrent/collection/ring_buffer.rb +59 -0
  47. data/lib/concurrent/collections.rb +3 -0
  48. data/lib/concurrent/configuration.rb +158 -0
  49. data/lib/concurrent/dataflow.rb +91 -0
  50. data/lib/concurrent/delay.rb +112 -0
  51. data/lib/concurrent/dereferenceable.rb +101 -0
  52. data/lib/concurrent/errors.rb +30 -0
  53. data/lib/concurrent/exchanger.rb +34 -0
  54. data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
  55. data/lib/concurrent/executor/executor.rb +229 -0
  56. data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
  57. data/lib/concurrent/executor/immediate_executor.rb +16 -0
  58. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  59. data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
  60. data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
  61. data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
  62. data/lib/concurrent/executor/per_thread_executor.rb +24 -0
  63. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  64. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
  65. data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
  66. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
  67. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
  68. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  69. data/lib/concurrent/executor/serialized_execution.rb +90 -0
  70. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  71. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  72. data/lib/concurrent/executor/timer_set.rb +143 -0
  73. data/lib/concurrent/executors.rb +9 -0
  74. data/lib/concurrent/future.rb +124 -0
  75. data/lib/concurrent/ivar.rb +111 -0
  76. data/lib/concurrent/logging.rb +17 -0
  77. data/lib/concurrent/mvar.rb +200 -0
  78. data/lib/concurrent/obligation.rb +171 -0
  79. data/lib/concurrent/observable.rb +40 -0
  80. data/lib/concurrent/options_parser.rb +46 -0
  81. data/lib/concurrent/promise.rb +169 -0
  82. data/lib/concurrent/scheduled_task.rb +78 -0
  83. data/lib/concurrent/supervisor.rb +343 -0
  84. data/lib/concurrent/timer_task.rb +341 -0
  85. data/lib/concurrent/tvar.rb +252 -0
  86. data/lib/concurrent/utilities.rb +3 -0
  87. data/lib/concurrent/utility/processor_count.rb +150 -0
  88. data/lib/concurrent/utility/timeout.rb +35 -0
  89. data/lib/concurrent/utility/timer.rb +21 -0
  90. data/lib/concurrent/version.rb +3 -0
  91. data/lib/concurrent_ruby.rb +1 -0
  92. data/lib/concurrent_ruby_ext.so +0 -0
  93. data/lib/extension_helper.rb +9 -0
  94. metadata +140 -0
@@ -0,0 +1,20 @@
1
+ module Concurrent
2
+ module Actress
3
+ # Allows quick creation of actors with behaviour defined by blocks.
4
+ # @example ping
5
+ # AdHoc.spawn :forward, an_actor do |where|
6
+ # # this block has to return proc defining #on_message behaviour
7
+ # -> message { where.tell message }
8
+ # end
9
+ class AdHoc
10
+ include 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
@@ -0,0 +1,98 @@
1
+ module Concurrent
2
+ module Actress
3
+
4
+ # This module is used to define actors. It can be included in any class,
5
+ # only requirement is to override {Context#on_message} method.
6
+ # @example ping
7
+ # class Ping
8
+ # include Context
9
+ # def on_message(message)
10
+ # message
11
+ # end
12
+ # end
13
+ #
14
+ # Ping.spawn(:ping1).ask(:m).value #=> :m
15
+ module Context
16
+ include TypeCheck
17
+ include CoreDelegations
18
+
19
+ attr_reader :core
20
+
21
+ # @abstract override to define Actor's behaviour
22
+ # @param [Object] message
23
+ # @return [Object] a result which will be used to set the IVar supplied to Reference#ask
24
+ # @note self should not be returned (or sent to other actors), {#reference} should be used
25
+ # instead
26
+ def on_message(message)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ # @api private
31
+ def on_envelope(envelope)
32
+ @envelope = envelope
33
+ on_message envelope.message
34
+ ensure
35
+ @envelope = nil
36
+ end
37
+
38
+ # @see Actress.spawn
39
+ def spawn(*args, &block)
40
+ Actress.spawn(*args, &block)
41
+ end
42
+
43
+ # @see Core#children
44
+ def children
45
+ core.children
46
+ end
47
+
48
+ # @see Core#terminate!
49
+ def terminate!
50
+ core.terminate!
51
+ end
52
+
53
+ # delegates to core.log
54
+ # @see Logging#log
55
+ def log(level, progname, message = nil, &block)
56
+ core.log(level, progname, message, &block)
57
+ end
58
+
59
+ private
60
+
61
+ def initialize_core(core)
62
+ @core = Type! core, Core
63
+ end
64
+
65
+ # @return [Envelope] current envelope, accessible inside #on_message processing
66
+ def envelope
67
+ @envelope or raise 'envelope not set'
68
+ end
69
+
70
+ def self.included(base)
71
+ base.extend ClassMethods
72
+ super base
73
+ end
74
+
75
+ module ClassMethods
76
+ # behaves as {Concurrent::Actress.spawn} but class_name is auto-inserted based on receiver
77
+ def spawn(name_or_opts, *args, &block)
78
+ Actress.spawn spawn_optionify(name_or_opts, *args), &block
79
+ end
80
+
81
+ # behaves as {Concurrent::Actress.spawn!} but class_name is auto-inserted based on receiver
82
+ def spawn!(name_or_opts, *args, &block)
83
+ Actress.spawn! spawn_optionify(name_or_opts, *args), &block
84
+ end
85
+
86
+ private
87
+
88
+ def spawn_optionify(name_or_opts, *args)
89
+ if name_or_opts.is_a? Hash
90
+ name_or_opts.merge class: self
91
+ else
92
+ { class: self, name: name_or_opts, args: args }
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,228 @@
1
+ module Concurrent
2
+ module Actress
3
+
4
+ require 'set'
5
+
6
+ # Core of the actor
7
+ # @note Whole class should be considered private. An user should use {Context}s and {Reference}s only.
8
+ # @note devel: core should not block on anything, e.g. it cannot wait on children to terminate
9
+ # that would eat up all threads in task pool and deadlock
10
+ class Core
11
+ include TypeCheck
12
+ include Concurrent::Logging
13
+
14
+ # @!attribute [r] reference
15
+ # @return [Reference] reference to this actor which can be safely passed around
16
+ # @!attribute [r] name
17
+ # @return [String] the name of this instance, it should be uniq (not enforced right now)
18
+ # @!attribute [r] path
19
+ # @return [String] a path of this actor. It is used for easier orientation and logging.
20
+ # Path is constructed recursively with: `parent.path + self.name` up to a {Actress::ROOT},
21
+ # e.g. `/an_actor/its_child`.
22
+ # (It will also probably form a supervision path (failures will be reported up to parents)
23
+ # in future versions.)
24
+ # @!attribute [r] executor
25
+ # @return [Executor] which is used to process messages
26
+ # @!attribute [r] terminated
27
+ # @return [Event] event which will become set when actor is terminated.
28
+ # @!attribute [r] actor_class
29
+ # @return [Context] a class including {Context} representing Actor's behaviour
30
+ attr_reader :reference, :name, :path, :executor, :terminated, :actor_class
31
+
32
+ # @option opts [String] name
33
+ # @option opts [Reference, nil] parent of an actor spawning this one
34
+ # @option opts [Context] actor_class a class to be instantiated defining Actor's behaviour
35
+ # @option opts [Array<Object>] args arguments for actor_class instantiation
36
+ # @option opts [Executor] executor, default is `Concurrent.configuration.global_task_pool`
37
+ # @option opts [IVar, nil] initialized, if present it'll be set or failed after {Context} initialization
38
+ # @option opts [Proc, nil] logger a proc accepting (level, progname, message = nil, &block) params,
39
+ # can be used to hook actor instance to any logging system
40
+ # @param [Proc] block for class instantiation
41
+ def initialize(opts = {}, &block)
42
+ @mailbox = Array.new
43
+ @serialized_execution = SerializedExecution.new
44
+ # noinspection RubyArgCount
45
+ @terminated = Event.new
46
+ @executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
47
+ @children = Set.new
48
+ @reference = Reference.new self
49
+ @name = (Type! opts.fetch(:name), String, Symbol).to_s
50
+ @actor = Concurrent::Atomic.new
51
+
52
+ parent = opts[:parent]
53
+ @parent_core = (Type! parent, Reference, NilClass) && parent.send(:core)
54
+ if @parent_core.nil? && @name != '/'
55
+ raise 'only root has no parent'
56
+ end
57
+
58
+ @path = @parent_core ? File.join(@parent_core.path, @name) : @name
59
+ @logger = opts[:logger]
60
+
61
+ @parent_core.add_child reference if @parent_core
62
+
63
+ @actor_class = actor_class = Child! opts.fetch(:class), Context
64
+ args = opts.fetch(:args, [])
65
+ initialized = Type! opts[:initialized], IVar, NilClass
66
+
67
+ schedule_execution do
68
+ begin
69
+ @actor.value = actor_class.new(*args, &block).
70
+ tap { |a| a.send :initialize_core, self }
71
+ initialized.set true if initialized
72
+ rescue => ex
73
+ log ERROR, ex
74
+ terminate!
75
+ initialized.fail ex if initialized
76
+ end
77
+ end
78
+ end
79
+
80
+ # @return [Reference, nil] of parent actor
81
+ def parent
82
+ @parent_core && @parent_core.reference
83
+ end
84
+
85
+ # @return [Array<Reference>] of children actors
86
+ def children
87
+ guard!
88
+ @children.to_a
89
+ end
90
+
91
+ # @api private
92
+ def add_child(child)
93
+ guard!
94
+ Type! child, Reference
95
+ @children.add child
96
+ nil
97
+ end
98
+
99
+ # @api private
100
+ def remove_child(child)
101
+ schedule_execution do
102
+ Type! child, Reference
103
+ @children.delete child
104
+ end
105
+ nil
106
+ end
107
+
108
+ # is executed by Reference scheduling processing of new messages
109
+ # can be called from other alternative Reference implementations
110
+ # @param [Envelope] envelope
111
+ def on_envelope(envelope)
112
+ schedule_execution do
113
+ if terminated?
114
+ reject_envelope envelope
115
+ else
116
+ @mailbox.push envelope
117
+ end
118
+ process_envelopes?
119
+ end
120
+ nil
121
+ end
122
+
123
+ # @note Actor rejects envelopes when terminated.
124
+ # @return [true, false] if actor is terminated
125
+ def terminated?
126
+ @terminated.set?
127
+ end
128
+
129
+ # Terminates the actor. Any Envelope received after termination is rejected.
130
+ # Terminates all its children, does not wait until they are terminated.
131
+ def terminate!
132
+ guard!
133
+ return nil if terminated?
134
+
135
+ @children.each do |ch|
136
+ ch.send(:core).tap { |core| core.send(:schedule_execution) { core.terminate! } }
137
+ end
138
+
139
+ @terminated.set
140
+
141
+ @parent_core.remove_child reference if @parent_core
142
+ @mailbox.each do |envelope|
143
+ reject_envelope envelope
144
+ log DEBUG, "rejected #{envelope.message} from #{envelope.sender_path}"
145
+ end
146
+ @mailbox.clear
147
+
148
+ nil
149
+ end
150
+
151
+ # @api private
152
+ # ensures that we are inside of the executor
153
+ def guard!
154
+ unless Actress.current == reference
155
+ raise "can be called only inside actor #{reference} but was #{Actress.current}"
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ # Ensures that only one envelope processing is scheduled with #schedule_execution,
162
+ # this allows other scheduled blocks to be executed before next envelope processing.
163
+ # Simply put this ensures that Core is still responsive to internal calls (like add_child)
164
+ # even though the Actor is flooded with messages.
165
+ def process_envelopes?
166
+ unless @mailbox.empty? || @receive_envelope_scheduled
167
+ @receive_envelope_scheduled = true
168
+ schedule_execution { receive_envelope }
169
+ end
170
+ end
171
+
172
+ # @return [Context]
173
+ def actor
174
+ @actor.value
175
+ end
176
+
177
+ # Processes single envelope, calls #process_envelopes? at the end to ensure next envelope
178
+ # scheduling.
179
+ def receive_envelope
180
+ envelope = @mailbox.shift
181
+
182
+ if terminated?
183
+ reject_envelope envelope
184
+ log FATAL, "this should not be happening #{caller[0]}"
185
+ end
186
+
187
+ log DEBUG, "received #{envelope.message} from #{envelope.sender_path}"
188
+
189
+ result = actor.on_envelope envelope
190
+ envelope.ivar.set result unless envelope.ivar.nil?
191
+
192
+ nil
193
+ rescue => error
194
+ log ERROR, error
195
+ terminate!
196
+ envelope.ivar.fail error unless envelope.ivar.nil?
197
+ ensure
198
+ @receive_envelope_scheduled = false
199
+ process_envelopes?
200
+ end
201
+
202
+ # Schedules blocks to be executed on executor sequentially,
203
+ # sets Actress.current
204
+ def schedule_execution
205
+ @serialized_execution.post(@executor) do
206
+ begin
207
+ Thread.current[:__current_actor__] = reference
208
+ yield
209
+ rescue => e
210
+ log FATAL, e
211
+ ensure
212
+ Thread.current[:__current_actor__] = nil
213
+ end
214
+ end
215
+
216
+ nil
217
+ end
218
+
219
+ def reject_envelope(envelope)
220
+ envelope.reject! ActressTerminated.new(reference)
221
+ end
222
+
223
+ def log(level, message = nil, &block)
224
+ super level, @path, message, &block
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,42 @@
1
+ module Concurrent
2
+ module Actress
3
+
4
+ # Provides publicly expose-able methods from {Core}.
5
+ module CoreDelegations
6
+ def name
7
+ core.name
8
+ end
9
+
10
+ def path
11
+ core.path
12
+ end
13
+
14
+ def parent
15
+ core.parent
16
+ end
17
+
18
+ def terminated?
19
+ core.terminated?
20
+ end
21
+
22
+ def terminated
23
+ core.terminated
24
+ end
25
+
26
+ def reference
27
+ core.reference
28
+ end
29
+
30
+ def executor
31
+ core.executor
32
+ end
33
+
34
+ def actor_class
35
+ core.actor_class
36
+ end
37
+
38
+ alias_method :ref, :reference
39
+ alias_method :actress_class, :actor_class
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ module Concurrent
2
+ module Actress
3
+ class Envelope
4
+ include TypeCheck
5
+
6
+ # @!attribute [r] message
7
+ # @return [Object] a message
8
+ # @!attribute [r] ivar
9
+ # @return [IVar] an ivar which becomes resolved after message is processed
10
+ # @!attribute [r] sender
11
+ # @return [Reference, Thread] an actor or thread sending the message
12
+ # @!attribute [r] address
13
+ # @return [Reference] where this message will be delivered
14
+
15
+ attr_reader :message, :ivar, :sender, :address
16
+
17
+ def initialize(message, ivar, sender, address)
18
+ @message = message
19
+ @ivar = Type! ivar, IVar, NilClass
20
+ @sender = Type! sender, Reference, Thread
21
+ @address = Type! address, Reference
22
+ end
23
+
24
+ def sender_path
25
+ if sender.is_a? Reference
26
+ sender.path
27
+ else
28
+ sender.to_s
29
+ end
30
+ end
31
+
32
+ def address_path
33
+ address.path
34
+ end
35
+
36
+ def reject!(error)
37
+ ivar.fail error unless ivar.nil?
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ module Concurrent
2
+ module Actress
3
+ Error = Class.new(StandardError)
4
+
5
+ class ActressTerminated < Error
6
+ include TypeCheck
7
+
8
+ def initialize(reference)
9
+ Type! reference, Reference
10
+ super reference.path
11
+ end
12
+ end
13
+ end
14
+ end