kulesa-celluloid 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ module Celluloid
2
+ # Manages a fixed-size pool of workers
3
+ module Worker
4
+ def self.included(klass)
5
+ klass.send :include, Celluloid
6
+ klass.send :extend, ClassMethods
7
+ end
8
+
9
+ # Class methods added to classes which include Celluloid::Worker
10
+ module ClassMethods
11
+ # Create a new pool of workers. Accepts the following options:
12
+ #
13
+ # * size: how many workers to create. Default is worker per CPU core
14
+ # * args: array of arguments to pass when creating a worker
15
+ #
16
+ def pool(options = {})
17
+ Manager.new(self, options)
18
+ end
19
+ end
20
+
21
+ # Delegates work (i.e. methods) and supervises workers
22
+ class Manager
23
+ include Celluloid
24
+ trap_exit :crash_handler
25
+
26
+ def initialize(worker_class, options = {})
27
+ @size = options[:size]
28
+ raise ArgumentError, "minimum pool size is 2" if @size && @size < 2
29
+
30
+ @size ||= [Celluloid.cores, 2].max
31
+ @args = options[:args] ? Array(options[:args]) : []
32
+
33
+ @worker_class = worker_class
34
+ @idle = @size.times.map { worker_class.new_link(*@args) }
35
+ end
36
+
37
+ # Execute the given method within a worker
38
+ def execute(method, *args, &block)
39
+ worker = provision_worker
40
+
41
+ begin
42
+ worker._send_ method, *args, &block
43
+ ensure
44
+ @idle << worker if worker.alive?
45
+ end
46
+ end
47
+
48
+ # Provision a new worker
49
+ def provision_worker
50
+ while @idle.empty?
51
+ # Using exclusive mode blocks incoming messages, so they don't pile
52
+ # up as waiting Celluloid::Tasks
53
+ response = exclusive { receive { |msg| msg.is_a? Response } }
54
+ Thread.current[:actor].handle_message(response)
55
+ end
56
+ @idle.shift
57
+ end
58
+
59
+ # Spawn a new worker for every crashed one
60
+ def crash_handler(actor, reason)
61
+ return unless reason # don't restart workers that exit cleanly
62
+ @idle << @worker_class.new_link(*@args)
63
+ end
64
+
65
+ def respond_to?(method)
66
+ super || (@worker_class ? @worker_class.instance_methods.include?(method.to_sym) : false)
67
+ end
68
+
69
+ def method_missing(method, *args, &block)
70
+ if respond_to?(method)
71
+ execute method, *args, &block
72
+ else
73
+ super
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
data/lib/celluloid.rb ADDED
@@ -0,0 +1,355 @@
1
+ require 'logger'
2
+ require 'thread'
3
+ require 'timeout'
4
+
5
+ module Celluloid
6
+ SHUTDOWN_TIMEOUT = 120 # How long actors have to terminate
7
+ @logger = Logger.new STDERR
8
+
9
+ class << self
10
+ attr_accessor :logger # Thread-safe logger class
11
+
12
+ def included(klass)
13
+ klass.send :extend, ClassMethods
14
+ end
15
+
16
+ # Are we currently inside of an actor?
17
+ def actor?
18
+ !!Thread.current[:actor]
19
+ end
20
+
21
+ # Is current actor running in exclusive mode?
22
+ def exclusive?
23
+ actor? and Thread.current[:actor].exclusive?
24
+ end
25
+
26
+ # Obtain the currently running actor (if one exists)
27
+ def current_actor
28
+ Actor.current
29
+ end
30
+
31
+ # Receive an asynchronous message
32
+ def receive(timeout = nil, &block)
33
+ actor = Thread.current[:actor]
34
+ if actor
35
+ actor.receive(timeout, &block)
36
+ else
37
+ Thread.mailbox.receive(timeout, &block)
38
+ end
39
+ end
40
+
41
+ # Sleep letting the actor continue processing messages
42
+ def sleep(interval)
43
+ actor = Thread.current[:actor]
44
+ if actor
45
+ actor.sleep(interval)
46
+ else
47
+ Kernel.sleep interval
48
+ end
49
+ end
50
+
51
+ # Generate a Universally Unique Identifier
52
+ def uuid
53
+ UUID.generate
54
+ end
55
+
56
+ # Obtain the number of CPUs in the system
57
+ def cores
58
+ CPUCounter.cores
59
+ end
60
+ alias_method :cpus, :cores
61
+ alias_method :ncpus, :cores
62
+
63
+ # Define an exception handler for actor crashes
64
+ def exception_handler(&block)
65
+ Logger.exception_handler(&block)
66
+ end
67
+
68
+ # Shut down all running actors
69
+ # FIXME: This should probably attempt a graceful shutdown of the supervision
70
+ # tree before iterating through all actors and telling them to terminate.
71
+ def shutdown
72
+ Timeout.timeout(SHUTDOWN_TIMEOUT) do
73
+ actors = Actor.all
74
+ Logger.info "Terminating #{actors.size} actors..." if actors.size > 0
75
+
76
+ # Attempt to shut down the supervision tree, if available
77
+ Supervisor.root.terminate if Supervisor.root
78
+
79
+ # Actors cannot self-terminate, you must do it for them
80
+ terminators = Actor.all.each do |actor|
81
+ begin
82
+ actor.future(:terminate)
83
+ rescue DeadActorError, MailboxError
84
+ end
85
+ end
86
+
87
+ terminators.each do |terminator|
88
+ begin
89
+ terminator.value
90
+ rescue DeadActorError, MailboxError
91
+ end
92
+ end
93
+
94
+ Logger.info "Shutdown completed cleanly"
95
+ end
96
+ end
97
+ end
98
+
99
+ # Terminate all actors at exit
100
+ at_exit { shutdown }
101
+
102
+ # Class methods added to classes which include Celluloid
103
+ module ClassMethods
104
+ # Create a new actor
105
+ def new(*args, &block)
106
+ proxy = Actor.new(allocate).proxy
107
+ proxy._send_(:initialize, *args, &block)
108
+ proxy
109
+ end
110
+ alias_method :spawn, :new
111
+
112
+ # Create a new actor and link to the current one
113
+ def new_link(*args, &block)
114
+ current_actor = Actor.current
115
+ raise NotActorError, "can't link outside actor context" unless current_actor
116
+
117
+ proxy = Actor.new(allocate).proxy
118
+ current_actor.link proxy
119
+ proxy._send_(:initialize, *args, &block)
120
+ proxy
121
+ end
122
+ alias_method :spawn_link, :new_link
123
+
124
+ # Create a supervisor which ensures an instance of an actor will restart
125
+ # an actor if it fails
126
+ def supervise(*args, &block)
127
+ Supervisor.supervise(self, *args, &block)
128
+ end
129
+
130
+ # Create a supervisor which ensures an instance of an actor will restart
131
+ # an actor if it fails, and keep the actor registered under a given name
132
+ def supervise_as(name, *args, &block)
133
+ Supervisor.supervise_as(name, self, *args, &block)
134
+ end
135
+
136
+ # Trap errors from actors we're linked to when they exit
137
+ def trap_exit(callback)
138
+ @exit_handler = callback.to_sym
139
+ end
140
+
141
+ # Obtain the exit handler for this actor
142
+ attr_reader :exit_handler
143
+
144
+ # Configure a custom mailbox factory
145
+ def use_mailbox(klass = nil, &block)
146
+ if block
147
+ @mailbox_factory = block
148
+ else
149
+ @mailbox_factory = proc { klass.new }
150
+ end
151
+ end
152
+
153
+ # Create a mailbox for this actor
154
+ def mailbox_factory
155
+ if defined?(@mailbox_factory)
156
+ @mailbox_factory.call
157
+ elsif defined?(super)
158
+ super
159
+ else
160
+ Mailbox.new
161
+ end
162
+ end
163
+
164
+ def ===(other)
165
+ other.kind_of? self
166
+ end
167
+ end
168
+
169
+ #
170
+ # Instance methods
171
+ #
172
+
173
+ # Is this actor alive?
174
+ def alive?
175
+ Thread.current[:actor].alive?
176
+ end
177
+
178
+ # Raise an exception in caller context, but stay running
179
+ def abort(cause)
180
+ raise AbortError.new(cause)
181
+ end
182
+
183
+ # Terminate this actor
184
+ def terminate
185
+ Thread.current[:actor].terminate
186
+ end
187
+
188
+ def inspect
189
+ str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
190
+ ivars = instance_variables.map do |ivar|
191
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
192
+ end
193
+
194
+ str << " " << ivars.join(' ') unless ivars.empty?
195
+ str << ">"
196
+ end
197
+
198
+ # Send a signal with the given name to all waiting methods
199
+ def signal(name, value = nil)
200
+ Thread.current[:actor].signal name, value
201
+ end
202
+
203
+ # Wait for the given signal
204
+ def wait(name)
205
+ Thread.current[:actor].wait name
206
+ end
207
+
208
+ # Obtain the current_actor
209
+ def current_actor
210
+ Actor.current
211
+ end
212
+
213
+ # Obtain the name of the current actor
214
+ def name
215
+ Actor.name
216
+ end
217
+
218
+ # Obtain the running tasks for this actor
219
+ def tasks
220
+ Thread.current[:actor].tasks.to_a
221
+ end
222
+
223
+ # Obtain the Ruby object the actor is wrapping. This should ONLY be used
224
+ # for a limited set of use cases like runtime metaprogramming. Interacting
225
+ # directly with the wrapped object foregoes any kind of thread safety that
226
+ # Celluloid would ordinarily provide you, and the object is guaranteed to
227
+ # be shared with at least the actor thread. Tread carefully.
228
+ def wrapped_object; self; end
229
+
230
+ # Obtain the Celluloid::Links for this actor
231
+ def links
232
+ Thread.current[:actor].links
233
+ end
234
+
235
+ # Link this actor to another, allowing it to crash or react to errors
236
+ def link(actor)
237
+ actor.notify_link Actor.current
238
+ notify_link actor
239
+ end
240
+
241
+ # Remove links to another actor
242
+ def unlink(actor)
243
+ actor.notify_unlink Actor.current
244
+ notify_unlink actor
245
+ end
246
+
247
+ def notify_link(actor)
248
+ links << actor
249
+ end
250
+
251
+ def notify_unlink(actor)
252
+ links.delete actor
253
+ end
254
+
255
+ # Is this actor linked to another?
256
+ def linked_to?(actor)
257
+ Thread.current[:actor].links.include? actor
258
+ end
259
+
260
+ # Receive an asynchronous message via the actor protocol
261
+ def receive(timeout = nil, &block)
262
+ Celluloid.receive(timeout, &block)
263
+ end
264
+
265
+ # Sleep while letting the actor continue to receive messages
266
+ def sleep(interval)
267
+ Celluloid.sleep(interval)
268
+ end
269
+
270
+ # Run given block in an exclusive mode: all synchronous calls block the whole
271
+ # actor, not only current message processing.
272
+ def exclusive(&block)
273
+ Thread.current[:actor].exclusive(&block)
274
+ end
275
+
276
+ # Call a block after a given interval, returning a Celluloid::Timer object
277
+ def after(interval, &block)
278
+ Thread.current[:actor].after(interval, &block)
279
+ end
280
+
281
+ # Call a block every given interval, returning a Celluloid::Timer object
282
+ def every(interval, &block)
283
+ Thread.current[:actor].every(interval, &block)
284
+ end
285
+
286
+ # Perform a blocking or computationally intensive action inside an
287
+ # asynchronous thread pool, allowing the caller to continue processing other
288
+ # messages in its mailbox in the meantime
289
+ def defer(&block)
290
+ # This implementation relies on the present implementation of
291
+ # Celluloid::Future, which uses a thread from InternalPool to run the block
292
+ Future.new(&block).value
293
+ end
294
+
295
+ # Handle async calls within an actor itself
296
+ def async(meth, *args, &block)
297
+ Actor.async Thread.current[:actor].mailbox, meth, *args, &block
298
+ end
299
+
300
+ # Handle calls to future within an actor itself
301
+ def future(meth, *args, &block)
302
+ Actor.future Thread.current[:actor].mailbox, meth, *args, &block
303
+ end
304
+
305
+ # Process async calls via method_missing
306
+ def method_missing(meth, *args, &block)
307
+ # bang methods are async calls
308
+ if meth.to_s.match(/!$/)
309
+ unbanged_meth = meth.to_s.sub(/!$/, '')
310
+ args.unshift unbanged_meth
311
+
312
+ call = AsyncCall.new(nil, :__send__, args, block)
313
+ begin
314
+ Thread.current[:actor].mailbox << call
315
+ rescue MailboxError
316
+ # Silently swallow asynchronous calls to dead actors. There's no way
317
+ # to reliably generate DeadActorErrors for async calls, so users of
318
+ # async calls should find other ways to deal with actors dying
319
+ # during an async call (i.e. linking/supervisors)
320
+ end
321
+
322
+ return
323
+ end
324
+
325
+ super
326
+ end
327
+ end
328
+
329
+ require 'celluloid/version'
330
+ require 'celluloid/actor_proxy'
331
+ require 'celluloid/calls'
332
+ require 'celluloid/core_ext'
333
+ require 'celluloid/cpu_counter'
334
+ require 'celluloid/events'
335
+ require 'celluloid/fiber'
336
+ require 'celluloid/fsm'
337
+ require 'celluloid/internal_pool'
338
+ require 'celluloid/links'
339
+ require 'celluloid/logger'
340
+ require 'celluloid/mailbox'
341
+ require 'celluloid/pool'
342
+ require 'celluloid/receivers'
343
+ require 'celluloid/registry'
344
+ require 'celluloid/responses'
345
+ require 'celluloid/signals'
346
+ require 'celluloid/task'
347
+ require 'celluloid/thread_handle'
348
+ require 'celluloid/timers'
349
+ require 'celluloid/uuid'
350
+
351
+ require 'celluloid/actor'
352
+ require 'celluloid/future'
353
+ require 'celluloid/group'
354
+ require 'celluloid/supervisor'
355
+ require 'celluloid/worker'