kulesa-celluloid 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ ![Celluloid](https://github.com/celluloid/celluloid/raw/master/logo.png)
2
+ =========
3
+ [![Build Status](https://secure.travis-ci.org/celluloid/celluloid.png?branch=master)](http://travis-ci.org/celluloid/celluloid)
4
+ [![Dependency Status](https://gemnasium.com/celluloid/celluloid.png)](https://gemnasium.com/celluloid/celluloid)
5
+
6
+ > "I thought of objects being like biological cells and/or individual
7
+ > computers on a network, only able to communicate with messages"
8
+ > _--Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"_
9
+
10
+ Celluloid provides a simple and natural way to build fault-tolerant concurrent
11
+ programs in Ruby. With Celluloid, you can build systems out of concurrent
12
+ objects just as easily as you build sequential programs out of regular objects.
13
+ Recommended for any developer, including novices, Celluloid should help ease
14
+ your worries about building multithreaded Ruby programs.
15
+
16
+ Much of the difficulty with building concurrent programs in Ruby arises because
17
+ the object-oriented mechanisms for structuring code, such as classes and
18
+ inheritance, are separate from the concurrency mechanisms, such as threads and
19
+ locks. Celluloid combines these into a single structure, an active object
20
+ running within a thread, called an "actor".
21
+
22
+ By combining concurrency with object oriented programming, Celluloid frees you
23
+ up from worry about where to use threads and locks. Celluloid combines them
24
+ together into a single concurrent object oriented programming model,
25
+ encapsulating state in concurrent objects and thus avoiding many of the
26
+ problems associated with multithreaded programming. Celluloid provides many
27
+ features which make concurrent programming simple, easy, and fun:
28
+
29
+ * __Automatic "deadlock-free" synchronization:__ Celluloid uses a concurrent
30
+ object model which combines method dispatch and thread synchronization.
31
+ Each actor is a concurrent object running in its own thread, and every method
32
+ invocation is wrapped in a fiber that can be suspended whenever it calls
33
+ out to other actors, and resumed when the response is available. This means
34
+ methods which are waiting for responses from other actors, external messages,
35
+ or other system events (including I/O with Celluloid::IO) can be suspended
36
+ and will never block other methods that are ready to run. This won't prevent
37
+ bugs in Celluloid, bugs in other thread-safe libraries you use, and even
38
+ certain "dangerous" features of Celluloid from causing your program to
39
+ deadlock, but in general, programs built with Celluloid will be naturally
40
+ immune to deadlocks.
41
+
42
+ * __Fault-tolerance:__ Celluloid has taken to heart many of Erlang's ideas
43
+ about fault-tolerance in order to enable self-healing applications.
44
+ The central idea: have you tried turning it off and on again? Celluloid
45
+ takes care of rebooting subcomponents of your application when they crash,
46
+ whether it's a single actor, or large (potentially multi-tiered) groups of
47
+ actors that are all interdependent. This means rather that worrying about
48
+ rescuing every last exception, you can just sit back, relax, and let parts
49
+ of your program crash, knowing Celluloid will automatically reboot them in
50
+ a clean state. Celluloid provides its own implementation of the core
51
+ fault-tolerance concepts in Erlang including [linking](https://github.com/celluloid/celluloid/wiki/Linking),
52
+ [supervisors](https://github.com/celluloid/celluloid/wiki/Supervisors),
53
+ and [supervision trees](https://github.com/celluloid/celluloid/wiki/Groups).
54
+
55
+ * __[Futures](https://github.com/celluloid/celluloid/wiki/futures):__
56
+ Ever wanted to call a method "in the background" and retrieve the
57
+ value it returns later? Celluloid futures do just that. It's like
58
+ calling ahead to a restaurant to place an order, so they can work
59
+ on preparing your food while you're on your way to pick it up.
60
+ When you ask for a method's return value, it's returned immediately
61
+ if the method has already completed, or otherwise the current method is
62
+ suspended until the value becomes available.
63
+
64
+ You can also build distributed systems with Celluloid using its
65
+ [sister project DCell](https://github.com/celluloid/dcell). Evented IO similar
66
+ to EventMachine (with a synchronous API) is available through the
67
+ [Celluloid::IO](https://github.com/celluloid/celluloid-io) library.
68
+
69
+ [Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki)
70
+ for more detailed documentation and usage notes.
71
+
72
+ Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
73
+ or visit us on IRC at #celluloid on freenode
74
+
75
+ ### Is it any good?
76
+
77
+ [Yes.](http://news.ycombinator.com/item?id=3067434)
78
+
79
+ ### Is It "Production Ready™"?
80
+
81
+ Yes, many users are now running Celluloid in production by using
82
+ [Sidekiq](https://github.com/mperham/sidekiq)
83
+
84
+ Supported Platforms
85
+ -------------------
86
+
87
+ Celluloid works on Ruby 1.9.3, JRuby 1.6, and Rubinius 2.0. JRuby or Rubinius
88
+ are the preferred platforms as they support true thread-level parallelism when
89
+ executing Ruby code, whereas MRI/YARV is constrained by a global interpreter
90
+ lock (GIL) and can only execute one thread at a time.
91
+
92
+ Celluloid requires Ruby 1.9 mode on all interpreters. This works out of the
93
+ box on MRI/YARV, and requires the following flags elsewhere:
94
+
95
+ * JRuby: --1.9 command line option, or JRUBY_OPTS=--1.9 environment variable
96
+ * rbx: -X19 command line option
97
+
98
+ Additional Reading
99
+ ------------------
100
+
101
+ * [Concurrent Object-Oriented Programming in Python with ATOM](http://python.org/workshops/1997-10/proceedings/atom/):
102
+ a similar system to Celluloid written in Python
103
+
104
+ Contributing to Celluloid
105
+ -------------------------
106
+
107
+ * Fork this repository on github
108
+ * Make your changes and send me a pull request
109
+ * If I like them I'll merge them
110
+ * If I've accepted a patch, feel free to ask for commit access
111
+
112
+ License
113
+ -------
114
+
115
+ Copyright (c) 2012 Tony Arcieri. Distributed under the MIT License. See
116
+ LICENSE.txt for further details.
@@ -0,0 +1,279 @@
1
+ module Celluloid
2
+ # Don't do Actor-like things outside Actor scope
3
+ class NotActorError < StandardError; end
4
+
5
+ # Trying to do something to a dead actor
6
+ class DeadActorError < StandardError; end
7
+
8
+ # The caller made an error, not the current actor
9
+ class AbortError < StandardError
10
+ attr_reader :cause
11
+
12
+ def initialize(cause)
13
+ @cause = cause
14
+ super "caused by #{cause.inspect}: #{cause.to_s}"
15
+ end
16
+ end
17
+
18
+ # Actors are Celluloid's concurrency primitive. They're implemented as
19
+ # normal Ruby objects wrapped in threads which communicate with asynchronous
20
+ # messages.
21
+ class Actor
22
+ extend Registry
23
+ attr_reader :subject, :proxy, :tasks, :links, :mailbox, :thread, :name
24
+
25
+ class << self
26
+ # Obtain the current actor
27
+ def current
28
+ actor = Thread.current[:actor]
29
+ raise NotActorError, "not in actor scope" unless actor
30
+ actor.proxy
31
+ end
32
+
33
+ # Obtain the name of the current actor
34
+ def name
35
+ actor = Thread.current[:actor]
36
+ raise NotActorError, "not in actor scope" unless actor
37
+ actor.name
38
+ end
39
+
40
+ # Invoke a method on the given actor via its mailbox
41
+ def call(mailbox, meth, *args, &block)
42
+ call = SyncCall.new(Thread.mailbox, meth, args, block)
43
+
44
+ begin
45
+ mailbox << call
46
+ rescue MailboxError
47
+ raise DeadActorError, "attempted to call a dead actor"
48
+ end
49
+
50
+ if Celluloid.actor? and not Celluloid.exclusive?
51
+ # The current task will be automatically resumed when we get a response
52
+ Task.suspend(:callwait).value
53
+ else
54
+ # Otherwise we're inside a normal thread, so block
55
+ response = Thread.mailbox.receive do |msg|
56
+ msg.respond_to?(:call) and msg.call == call
57
+ end
58
+
59
+ response.value
60
+ end
61
+ end
62
+
63
+ # Invoke a method asynchronously on an actor via its mailbox
64
+ def async(mailbox, meth, *args, &block)
65
+ begin
66
+ mailbox << AsyncCall.new(Thread.mailbox, meth, args, block)
67
+ rescue MailboxError
68
+ # Silently swallow asynchronous calls to dead actors. There's no way
69
+ # to reliably generate DeadActorErrors for async calls, so users of
70
+ # async calls should find other ways to deal with actors dying
71
+ # during an async call (i.e. linking/supervisors)
72
+ end
73
+ end
74
+
75
+ # Call a method asynchronously and retrieve its value later
76
+ def future(mailbox, meth, *args, &block)
77
+ future = Future.new
78
+ future.execute(mailbox, meth, args, block)
79
+ future
80
+ end
81
+
82
+ # Obtain all running actors in the system
83
+ def all
84
+ actors = []
85
+ Thread.list.each do |t|
86
+ actor = t[:actor]
87
+ actors << actor.proxy if actor
88
+ end
89
+ actors
90
+ end
91
+ end
92
+
93
+ # Wrap the given subject with an Actor
94
+ def initialize(subject)
95
+ @subject = subject
96
+ @mailbox = subject.class.mailbox_factory
97
+ @tasks = Set.new
98
+ @links = Links.new
99
+ @signals = Signals.new
100
+ @receivers = Receivers.new
101
+ @timers = Timers.new
102
+ @running = true
103
+ @exclusive = false
104
+ @name = nil
105
+
106
+ @thread = ThreadHandle.new do
107
+ Thread.current[:actor] = self
108
+ Thread.current[:mailbox] = @mailbox
109
+ run
110
+ end
111
+
112
+ @proxy = ActorProxy.new(self)
113
+ end
114
+
115
+ # Is this actor running in exclusive mode?
116
+ def exclusive?
117
+ @exclusive
118
+ end
119
+
120
+ # Execute a code block in exclusive mode.
121
+ def exclusive
122
+ @exclusive = true
123
+ yield
124
+ ensure
125
+ @exclusive = false
126
+ end
127
+
128
+ # Terminate this actor
129
+ def terminate
130
+ @running = false
131
+ end
132
+
133
+ # Send a signal with the given name to all waiting methods
134
+ def signal(name, value = nil)
135
+ @signals.send name, value
136
+ end
137
+
138
+ # Wait for the given signal
139
+ def wait(name)
140
+ @signals.wait name
141
+ end
142
+
143
+ # Receive an asynchronous message
144
+ def receive(timeout = nil, &block)
145
+ @receivers.receive(timeout, &block)
146
+ end
147
+
148
+ # Run the actor loop
149
+ def run
150
+ begin
151
+ while @running
152
+ begin
153
+ message = @mailbox.receive(timeout)
154
+ rescue ExitEvent => exit_event
155
+ Task.new(:exit_handler) { handle_exit_event exit_event }.resume
156
+ retry
157
+ rescue NamingRequest => ex
158
+ @name = ex.name
159
+ retry
160
+ rescue TerminationRequest
161
+ break
162
+ end
163
+
164
+ if message
165
+ handle_message message
166
+ else
167
+ # No message indicates a timeout
168
+ @timers.fire
169
+ @receivers.fire_timers
170
+ end
171
+ end
172
+ rescue MailboxShutdown
173
+ # If the mailbox detects shutdown, exit the actor
174
+ end
175
+
176
+ shutdown
177
+ rescue Exception => ex
178
+ handle_crash(ex)
179
+ raise unless ex.is_a? StandardError
180
+ end
181
+
182
+ # How long to wait until the next timer fires
183
+ def timeout
184
+ i1 = @timers.wait_interval
185
+ i2 = @receivers.wait_interval
186
+
187
+ if i1 and i2
188
+ i1 < i2 ? i1 : i2
189
+ elsif i1
190
+ i1
191
+ else
192
+ i2
193
+ end
194
+ end
195
+
196
+ # Schedule a block to run at the given time
197
+ def after(interval)
198
+ @timers.add(interval) do
199
+ Task.new(:timer) { yield }.resume
200
+ end
201
+ end
202
+
203
+ # Schedule a block to run at the given time
204
+ def every(interval)
205
+ @timers.add(interval, true) do
206
+ Task.new(:timer) { yield }.resume
207
+ end
208
+ end
209
+
210
+ # Sleep for the given amount of time
211
+ def sleep(interval)
212
+ if Celluloid.exclusive?
213
+ Kernel.sleep(interval)
214
+ else
215
+ task = Task.current
216
+ @timers.add(interval) { task.resume }
217
+ Task.suspend :sleeping
218
+ end
219
+ end
220
+
221
+ # Handle an incoming message
222
+ def handle_message(message)
223
+ case message
224
+ when Call
225
+ Task.new(:message_handler) { message.dispatch(@subject) }.resume
226
+ when Response
227
+ message.call.task.resume message
228
+ else
229
+ @receivers.handle_message(message)
230
+ end
231
+ message
232
+ end
233
+
234
+ # Handle exit events received by this actor
235
+ def handle_exit_event(exit_event)
236
+ exit_handler = @subject.class.exit_handler
237
+ if exit_handler
238
+ return @subject.send(exit_handler, exit_event.actor, exit_event.reason)
239
+ end
240
+
241
+ # Reraise exceptions from linked actors
242
+ # If no reason is given, actor terminated cleanly
243
+ raise exit_event.reason if exit_event.reason
244
+ end
245
+
246
+ # Handle any exceptions that occur within a running actor
247
+ def handle_crash(exception)
248
+ Logger.crash("#{@subject.class} crashed!", exception)
249
+ shutdown ExitEvent.new(@proxy, exception)
250
+ rescue => ex
251
+ Logger.crash("#{@subject.class}: ERROR HANDLER CRASHED!", ex)
252
+ end
253
+
254
+ # Handle cleaning up this actor after it exits
255
+ def shutdown(exit_event = ExitEvent.new(@proxy))
256
+ run_finalizer
257
+ cleanup exit_event
258
+ ensure
259
+ Thread.current[:actor] = nil
260
+ Thread.current[:mailbox] = nil
261
+ end
262
+
263
+ # Run the user-defined finalizer, if one is set
264
+ def run_finalizer
265
+ @subject.finalize if @subject.respond_to? :finalize
266
+ rescue => ex
267
+ Logger.crash("#{@subject.class}#finalize crashed!", ex)
268
+ end
269
+
270
+ # Clean up after this actor
271
+ def cleanup(exit_event)
272
+ @mailbox.shutdown
273
+ @links.send_event exit_event
274
+ tasks.each { |task| task.terminate }
275
+ rescue => ex
276
+ Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,95 @@
1
+ module Celluloid
2
+ # A proxy object returned from Celluloid::Actor.spawn/spawn_link which
3
+ # dispatches calls and casts to normal Ruby objects which are running inside
4
+ # of their own threads.
5
+ class ActorProxy
6
+ attr_reader :mailbox
7
+
8
+ def initialize(actor)
9
+ @mailbox, @thread, @klass = actor.mailbox, actor.thread, actor.subject.class.to_s
10
+
11
+ # Cache "unbanged" versions of methods, e.g. :foobar! => :foobar
12
+ @unbanged_methods = {}
13
+ end
14
+
15
+ def _send_(meth, *args, &block)
16
+ Actor.call @mailbox, :__send__, meth, *args, &block
17
+ end
18
+
19
+ def class
20
+ Actor.call @mailbox, :__send__, :class
21
+ end
22
+
23
+ def name
24
+ Actor.call @mailbox, :name
25
+ end
26
+
27
+ def is_a?(klass)
28
+ Actor.call @mailbox, :is_a?, klass
29
+ end
30
+
31
+ def kind_of?(klass)
32
+ Actor.call @mailbox, :kind_of?, klass
33
+ end
34
+
35
+ def respond_to?(meth)
36
+ Actor.call @mailbox, :respond_to?, meth
37
+ end
38
+
39
+ def methods(include_ancestors = true)
40
+ Actor.call @mailbox, :methods, include_ancestors
41
+ end
42
+
43
+ def alive?
44
+ @mailbox.alive?
45
+ end
46
+
47
+ def to_s
48
+ Actor.call @mailbox, :to_s
49
+ end
50
+
51
+ def inspect
52
+ Actor.call @mailbox, :inspect
53
+ rescue DeadActorError
54
+ "#<Celluloid::Actor(#{@klass}) dead>"
55
+ end
56
+
57
+ # Make an asynchronous call to an actor, for those who don't like the
58
+ # predicate syntax. TIMTOWTDI!
59
+ def async(method_name, *args, &block)
60
+ Actor.async @mailbox, method_name, *args, &block
61
+ end
62
+
63
+ # Create a Celluloid::Future which calls a given method
64
+ def future(method_name, *args, &block)
65
+ Actor.future @mailbox, method_name, *args, &block
66
+ end
67
+
68
+ # Terminate the associated actor
69
+ def terminate
70
+ terminate!
71
+ Thread.pass while alive?
72
+ end
73
+
74
+ # Terminate the associated actor asynchronously
75
+ def terminate!
76
+ raise DeadActorError, "actor already terminated" unless alive?
77
+ @mailbox.system_event TerminationRequest.new
78
+ end
79
+
80
+ # method_missing black magic to call bang predicate methods asynchronously
81
+ def method_missing(meth, *args, &block)
82
+ # bang methods are async calls
83
+ if meth.match(/!$/)
84
+ # This operation is idempotent and therefore thread-safe
85
+ # The worst case is that the string transformation on the right will
86
+ # run multiple times and make a little more work for the GC
87
+ meth = @unbanged_methods[meth] ||= meth.to_s.sub(/!$/, '').to_sym
88
+
89
+ Actor.async @mailbox, meth, *args, &block
90
+ else
91
+ Actor.call @mailbox, meth, *args, &block
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,105 @@
1
+ module Celluloid
2
+ # Calls represent requests to an actor
3
+ class Call
4
+ attr_reader :caller, :method, :arguments, :block
5
+
6
+ def initialize(caller, method, arguments = [], block = nil)
7
+ @caller, @method, @arguments, @block = caller, method, arguments, block
8
+ end
9
+
10
+ def check_signature(obj)
11
+ unless obj.respond_to? @method
12
+ raise NoMethodError, "undefined method `#{@method}' for #{obj.inspect}"
13
+ end
14
+
15
+ begin
16
+ arity = obj.method(@method).arity
17
+ rescue NameError
18
+ # If the object claims it responds to a method, but it doesn't exist,
19
+ # then we have to assume method_missing will do what it says
20
+ @arguments.unshift(@method)
21
+ @method = :method_missing
22
+ return
23
+ end
24
+
25
+ if arity >= 0
26
+ if arguments.size != arity
27
+ raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{arity})"
28
+ end
29
+ elsif arity < -1
30
+ mandatory_args = -arity - 1
31
+ if arguments.size < mandatory_args
32
+ raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{mandatory_args})"
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ # Synchronous calls wait for a response
39
+ class SyncCall < Call
40
+ attr_reader :task
41
+
42
+ def initialize(caller, method, arguments = [], block = nil, task = Fiber.current.task)
43
+ super(caller, method, arguments, block)
44
+ @task = task
45
+ end
46
+
47
+ def dispatch(obj)
48
+ begin
49
+ check_signature(obj)
50
+ rescue => ex
51
+ respond ErrorResponse.new(self, AbortError.new(ex))
52
+ return
53
+ end
54
+
55
+ begin
56
+ result = obj.send @method, *@arguments, &@block
57
+ rescue Exception => exception
58
+ # Exceptions that occur during synchronous calls are reraised in the
59
+ # context of the caller
60
+ respond ErrorResponse.new(self, exception)
61
+
62
+ if exception.is_a? AbortError
63
+ # Aborting indicates a protocol error on the part of the caller
64
+ # It should crash the caller, but the exception isn't reraised
65
+ return
66
+ else
67
+ # Otherwise, it's a bug in this actor and should be reraised
68
+ raise exception
69
+ end
70
+ end
71
+
72
+ respond SuccessResponse.new(self, result)
73
+ end
74
+
75
+ def cleanup
76
+ exception = DeadActorError.new("attempted to call a dead actor")
77
+ respond ErrorResponse.new(self, exception)
78
+ end
79
+
80
+ def respond(message)
81
+ @caller << message
82
+ rescue MailboxError
83
+ # It's possible the caller exited or crashed before we could send a
84
+ # response to them.
85
+ end
86
+ end
87
+
88
+ # Asynchronous calls don't wait for a response
89
+ class AsyncCall < Call
90
+ def dispatch(obj)
91
+ begin
92
+ check_signature(obj)
93
+ rescue Exception => ex
94
+ Logger.crash("#{obj.class}: async call `#{@method}' failed!", ex)
95
+ return
96
+ end
97
+
98
+ obj.send(@method, *@arguments, &@block)
99
+ rescue AbortError => ex
100
+ # Swallow aborted async calls, as they indicate the caller made a mistake
101
+ Logger.crash("#{obj.class}: async call `#{@method}' aborted!", ex)
102
+ end
103
+ end
104
+ end
105
+
@@ -0,0 +1,25 @@
1
+ require 'celluloid/fiber'
2
+
3
+ # Monkeypatch Thread to allow lazy access to its Celluloid::Mailbox
4
+ class Thread
5
+ attr_accessor :uuid_counter, :uuid_limit
6
+
7
+ # Retrieve the mailbox for the current thread or lazily initialize it
8
+ def self.mailbox
9
+ current[:mailbox] ||= Celluloid::Mailbox.new
10
+ end
11
+
12
+ # Receive a message either as an actor or through the local mailbox
13
+ def self.receive(timeout = nil, &block)
14
+ if Celluloid.actor?
15
+ Celluloid.receive(timeout, &block)
16
+ else
17
+ mailbox.receive(timeout, &block)
18
+ end
19
+ end
20
+ end
21
+
22
+ class Fiber
23
+ # Celluloid::Task associated with this Fiber
24
+ attr_accessor :task
25
+ end
@@ -0,0 +1,16 @@
1
+ require 'rbconfig'
2
+
3
+ module Celluloid
4
+ module CPUCounter
5
+ case RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
6
+ when 'darwin'
7
+ @cores = Integer(`sysctl hw.ncpu`[/\d+/])
8
+ when 'linux'
9
+ @cores = File.read("/proc/cpuinfo").scan(/core id\s+: \d+/).uniq.size
10
+ else
11
+ @cores = nil
12
+ end
13
+
14
+ def self.cores; @cores; end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module Celluloid
2
+ # Exceptional system events which need to be processed out of band
3
+ class SystemEvent < Exception; end
4
+
5
+ # An actor has exited for the given reason
6
+ class ExitEvent < SystemEvent
7
+ attr_reader :actor, :reason
8
+
9
+ def initialize(actor, reason = nil)
10
+ @actor, @reason = actor, reason
11
+ super reason.to_s
12
+ end
13
+ end
14
+
15
+ # Name an actor at the time it's registered
16
+ class NamingRequest < SystemEvent
17
+ attr_reader :name
18
+
19
+ def initialize(name)
20
+ @name = name
21
+ end
22
+ end
23
+
24
+ # Request for an actor to terminate
25
+ class TerminationRequest < SystemEvent; end
26
+ end
@@ -0,0 +1,32 @@
1
+ # Fibers are hard... let's go shopping!
2
+ begin
3
+ require 'fiber'
4
+ rescue LoadError => ex
5
+ if defined? JRUBY_VERSION
6
+ if RUBY_VERSION < "1.9.2"
7
+ raise LoadError, "Celluloid requires JRuby 1.9 mode. Please pass the --1.9 flag or set JRUBY_OPTS=--1.9"
8
+ end
9
+
10
+ # Fibers are broken on JRuby 1.6.5. This works around the issue
11
+ if JRUBY_VERSION[/^1\.6\.5/]
12
+ require 'jruby'
13
+ org.jruby.ext.fiber.FiberExtLibrary.new.load(JRuby.runtime, false)
14
+ class org::jruby::ext::fiber::ThreadFiber
15
+ field_accessor :state
16
+ end
17
+
18
+ class Fiber
19
+ def alive?
20
+ JRuby.reference(self).state != org.jruby.ext.fiber.ThreadFiberState::FINISHED
21
+ end
22
+ end
23
+ else
24
+ # Just in case subsequent JRuby releases have broken fibers :/
25
+ raise ex
26
+ end
27
+ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
28
+ raise LoadError, "Celluloid requires Rubinius 1.9 mode. Please pass the -X19 flag."
29
+ else
30
+ raise ex
31
+ end
32
+ end