zoidberg 0.1.12 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +83 -15
- data/lib/zoidberg.rb +3 -2
- data/lib/zoidberg/pool.rb +11 -1
- data/lib/zoidberg/proxy.rb +57 -24
- data/lib/zoidberg/proxy/confined.rb +4 -4
- data/lib/zoidberg/proxy/liberated.rb +33 -18
- data/lib/zoidberg/shell.rb +59 -9
- data/lib/zoidberg/signal.rb +9 -0
- data/lib/zoidberg/supervise.rb +0 -35
- data/lib/zoidberg/timer.rb +12 -5
- data/lib/zoidberg/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f631edac70299825464b22bcb61b054248eb0da
|
4
|
+
data.tar.gz: 841685d0521b5b932c542db06d6d922a74d6c5ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4da51a62307d58f60fb8947337f41e45786bcd3b364b6ada62c6f5f2c3dc225afd1193b8f020d4dcc00da6b9eab459520182ff4b6818993a753b67536c4c543
|
7
|
+
data.tar.gz: 76b9c10d8f7896f66eb5640e7218bc84779e7df993dccb2807832b874729150c2e1979ca78601d9ad1d91429b2420839276c5c58716fd55011738d694018128a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# v0.2.0
|
2
|
+
* Update termination behaviors to ensure expected results
|
3
|
+
* Provide and use flag for system shutdown to disable supervision
|
4
|
+
* Proper termination behavior when not supervised
|
5
|
+
* Prevent pool from dying on worker exceptions
|
6
|
+
|
1
7
|
# v0.1.12
|
2
8
|
* Move event signal to abstract proxy
|
3
9
|
* Update timeout library usage
|
data/README.md
CHANGED
@@ -128,6 +128,9 @@ Maybe?
|
|
128
128
|
|
129
129
|
## Features
|
130
130
|
|
131
|
+
Originally, we looked at just adding safety but this library provides
|
132
|
+
a handful more of things.
|
133
|
+
|
131
134
|
### Implicit Locking
|
132
135
|
|
133
136
|
Zoidberg automatically synchronizes requests made to an instance. This
|
@@ -150,6 +153,84 @@ This shell is still in development and not fully supported yet. The
|
|
150
153
|
hard shell is an implementation that is more reflective of the actor
|
151
154
|
model with a single thread wrapping an instance and synchronizing access.
|
152
155
|
|
156
|
+
### Garbage Collection
|
157
|
+
|
158
|
+
Garbage collection happens as usual with Zoidberg. When an instance is created
|
159
|
+
the result may look like the instance but really it is a proxy wrapping the
|
160
|
+
raw instance. When the proxy falls out of scope and is garbage collected the
|
161
|
+
raw instance it wrapped will also fall out of scope and be garbage collected.
|
162
|
+
This wrapping behavior is what allows supervised instances to be automatically
|
163
|
+
swapped out on failure state without requiring intervention. It also introduces
|
164
|
+
the ability to add support for destructors, which is pretty cool.
|
165
|
+
|
166
|
+
#### Destructors
|
167
|
+
|
168
|
+
Instances can define destructors via the `#terminate` method. When the instance
|
169
|
+
is garbage collected, the `#terminate` method will be called prior to the instance
|
170
|
+
falling out of scope and being removed from the system. This allows the introduction
|
171
|
+
of destructors:
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
|
175
|
+
class Fubar
|
176
|
+
include Zoidberg::Shell
|
177
|
+
|
178
|
+
...
|
179
|
+
|
180
|
+
def terminate
|
181
|
+
puts "I am being garbage collected!"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
### Signals
|
187
|
+
|
188
|
+
Simple signals are available as well as signals pushing data.
|
189
|
+
|
190
|
+
#### Simple Signals
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
sig = Zoidberg::Signal.new
|
194
|
+
Thread.new do
|
195
|
+
sig.wait(:go)
|
196
|
+
puts 'Done!'
|
197
|
+
end
|
198
|
+
puts 'Ready to signal!'
|
199
|
+
sleep(1)
|
200
|
+
sig.signal(:go)
|
201
|
+
puts 'Signal sent'
|
202
|
+
```
|
203
|
+
|
204
|
+
#### Simple Broadcasting
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
sig = Zoidberg::Signal.new
|
208
|
+
5.times do
|
209
|
+
Thread.new do
|
210
|
+
sig.wait(:go)
|
211
|
+
puts 'Done!'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
puts 'Ready to signal!'
|
215
|
+
sleep(1)
|
216
|
+
sig.broadcast(:go)
|
217
|
+
puts 'Broadcast sent'
|
218
|
+
```
|
219
|
+
|
220
|
+
#### Pushing data
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
sig = Zoidberg::Signal.new
|
224
|
+
Thread.new do
|
225
|
+
value = sig.wait(:go)
|
226
|
+
puts "Done! Received: #{value.inspect}"
|
227
|
+
end
|
228
|
+
puts 'Ready to signal!'
|
229
|
+
sleep(1)
|
230
|
+
sig.signal(:go, :ohai)
|
231
|
+
puts 'Signal sent'
|
232
|
+
```
|
233
|
+
|
153
234
|
### Supervision
|
154
235
|
|
155
236
|
Zoidberg can provide instance supervision. To enable supervision on a
|
@@ -166,14 +247,8 @@ This will implicitly load the `Zoidberg::Shell` module and new instances
|
|
166
247
|
will be supervised. Supervision means Zoidberg will watch for unexpected
|
167
248
|
exceptions. What are "unexpected exceptions"? They are any exception raised
|
168
249
|
via `raise`. This will cause the instance to be torn down and a new instance
|
169
|
-
to be instantiated.
|
170
|
-
|
171
|
-
### Supervision
|
172
|
-
|
173
|
-
Zoidberg provides lazy supervision. There is no single supervisor. Instead
|
174
|
-
supervision is handled by the proxy which wraps the class. Failure of an
|
175
|
-
instance will result in termination + reinstantiation. When externally
|
176
|
-
accessing the instance nothing requires modification.
|
250
|
+
to be instantiated. To the outside observer, nothing will change and no
|
251
|
+
modification is required.
|
177
252
|
|
178
253
|
### Pools
|
179
254
|
|
@@ -181,10 +256,3 @@ Zoidberg allows pooling lazy supervised instances. Unexpected failures will
|
|
181
256
|
cause the instance to be terminated and re-initialized as usual. The pool
|
182
257
|
will deliver requests to free instances, or queue them until a free instance
|
183
258
|
is available.
|
184
|
-
|
185
|
-
### Garbage Collection
|
186
|
-
|
187
|
-
Garbage collection happens as usual with Zoidberg. When an instance is created
|
188
|
-
the result may look like the instance but really it is proxy wrapping the
|
189
|
-
raw instance. When the proxy falls out of scope and is garbage collected the
|
190
|
-
raw instance it wrapped will also fall out of scope and be garbage collected.
|
data/lib/zoidberg.rb
CHANGED
@@ -66,8 +66,9 @@ Zoidberg.logger = Zoidberg::Logger.new(STDERR)
|
|
66
66
|
Zoidberg.default_shell = Zoidberg::SoftShell
|
67
67
|
|
68
68
|
%w(INT TERM).each do |sig_name|
|
69
|
-
Signal.trap(sig_name) do |*_|
|
70
|
-
Zoidberg.signal_shutdown = true
|
69
|
+
original = Signal.trap(sig_name) do |*_|
|
70
|
+
Zoidberg.signal_shutdown = true unless ENV['ZOIDBERG_TESTING']
|
71
|
+
original.call if original.respond_to?(:call)
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
data/lib/zoidberg/pool.rb
CHANGED
@@ -79,7 +79,17 @@ module Zoidberg
|
|
79
79
|
def method_missing(*args, &block)
|
80
80
|
worker = _zoidberg_free_worker
|
81
81
|
current_self._release_lock!
|
82
|
-
|
82
|
+
begin
|
83
|
+
worker.send(*args, &block)
|
84
|
+
rescue Zoidberg::DeadException => e
|
85
|
+
if(e.origin_object_id == object_id)
|
86
|
+
raise e
|
87
|
+
else
|
88
|
+
abort e
|
89
|
+
end
|
90
|
+
rescue => e
|
91
|
+
abort e
|
92
|
+
end
|
83
93
|
end
|
84
94
|
|
85
95
|
# Find or wait for a free worker
|
data/lib/zoidberg/proxy.rb
CHANGED
@@ -53,6 +53,10 @@ module Zoidberg
|
|
53
53
|
raise NotImplementedError
|
54
54
|
end
|
55
55
|
|
56
|
+
def _zoidberg_unsupervise
|
57
|
+
@_supervised = false
|
58
|
+
end
|
59
|
+
|
56
60
|
# Set the raw instance into the proxy and link proxy to instance
|
57
61
|
#
|
58
62
|
# @param inst [Object] raw instance being wrapped
|
@@ -124,21 +128,15 @@ module Zoidberg
|
|
124
128
|
def _zoidberg_unexpected_error(e)
|
125
129
|
::Zoidberg.logger.error "Unexpected exception: #{e.class} - #{e}"
|
126
130
|
::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
127
|
-
|
128
|
-
if(_zoidberg_link)
|
129
|
-
|
130
|
-
|
131
|
-
_zoidberg_link.
|
132
|
-
|
133
|
-
)
|
134
|
-
end
|
135
|
-
else
|
136
|
-
if(@_supervised)
|
137
|
-
::Zoidberg.logger.warn "Unexpected error for supervised class `#{@_raw_instance.class.name}`. Handling error (#{e.class} - #{e})"
|
138
|
-
::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
139
|
-
_zoidberg_handle_unexpected_error(e)
|
140
|
-
end
|
131
|
+
if(_zoidberg_link)
|
132
|
+
if(_zoidberg_link.class.trap_exit)
|
133
|
+
::Zoidberg.logger.warn "Calling linked exit trapper #{@_raw_instance.class.name} -> #{_zoidberg_link.class}: #{e.class} - #{e}"
|
134
|
+
_zoidberg_link.async.send(
|
135
|
+
_zoidberg_link.class.trap_exit, @_raw_instance, e
|
136
|
+
)
|
141
137
|
end
|
138
|
+
else
|
139
|
+
_zoidberg_handle_unexpected_error(e)
|
142
140
|
end
|
143
141
|
end
|
144
142
|
|
@@ -156,20 +154,23 @@ module Zoidberg
|
|
156
154
|
# @param error [Exception] exception that was caught
|
157
155
|
# @return [TrueClass]
|
158
156
|
def _zoidberg_handle_unexpected_error(error)
|
159
|
-
|
160
|
-
|
157
|
+
if(_raw_instance.respond_to?(:restart))
|
158
|
+
unless(::Zoidberg.in_shutdown?)
|
161
159
|
begin
|
162
160
|
_raw_instance.restart(error)
|
163
161
|
return # short circuit
|
164
162
|
rescue => e
|
165
163
|
end
|
166
164
|
end
|
167
|
-
|
165
|
+
end
|
166
|
+
_zoidberg_destroy!
|
167
|
+
if(@_supervised && !::Zoidberg.in_shutdown?)
|
168
168
|
_aquire_lock!
|
169
169
|
begin
|
170
170
|
args = _build_args.dup
|
171
171
|
inst = args.shift.unshelled_new(*args.first, &args.last)
|
172
172
|
_zoidberg_set_instance(inst)
|
173
|
+
::Zoidberg.logger.debug "Supervised instance has been rebuilt: #{inst}"
|
173
174
|
if(_raw_instance.respond_to?(:restarted!))
|
174
175
|
_raw_instance.restarted!
|
175
176
|
end
|
@@ -188,20 +189,30 @@ module Zoidberg
|
|
188
189
|
# @return [TrueClass]
|
189
190
|
def _zoidberg_destroy!(error=nil, &block)
|
190
191
|
unless(_raw_instance.respond_to?(:_zoidberg_destroyed))
|
192
|
+
if(::Zoidberg.in_shutdown?)
|
193
|
+
@_zoidberg_timer.terminate if @_zoidberg_timer
|
194
|
+
@_zoidberg_signal.terminate if @_zoidberg_signal
|
195
|
+
end
|
191
196
|
if(_raw_instance.respond_to?(:terminate))
|
192
|
-
|
193
|
-
_raw_instance.terminate
|
194
|
-
|
195
|
-
|
197
|
+
begin
|
198
|
+
if(_raw_instance.method(:terminate).arity == 0)
|
199
|
+
_raw_instance.terminate
|
200
|
+
else
|
201
|
+
_raw_instance.terminate(error)
|
202
|
+
end
|
203
|
+
rescue => e
|
204
|
+
::Zoidberg.logger.error "Unexpected exception caught during terminatation of #{self}: #{e}"
|
205
|
+
::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
196
206
|
end
|
197
207
|
end
|
208
|
+
block.call if block
|
209
|
+
oid = _raw_instance.object_id
|
198
210
|
death_from_above = ::Proc.new do |*_|
|
199
|
-
::Kernel.raise ::Zoidberg::DeadException.new('Instance in terminated state!')
|
211
|
+
::Kernel.raise ::Zoidberg::DeadException.new('Instance in terminated state!', oid)
|
200
212
|
end
|
201
213
|
death_from_above_display = ::Proc.new do
|
202
214
|
"#<#{self.class.name}:TERMINATED>"
|
203
215
|
end
|
204
|
-
block.call if block
|
205
216
|
_raw_instance.instance_variables.each do |i_var|
|
206
217
|
_raw_instance.remove_instance_variable(i_var)
|
207
218
|
end
|
@@ -219,7 +230,12 @@ module Zoidberg
|
|
219
230
|
end
|
220
231
|
true
|
221
232
|
end
|
222
|
-
|
233
|
+
|
234
|
+
def terminate
|
235
|
+
_zoidberg_unsupervise
|
236
|
+
_zoidberg_destroy!
|
237
|
+
end
|
238
|
+
# alias_method :terminate, :_zoidberg_destroy!
|
223
239
|
|
224
240
|
# @return [self]
|
225
241
|
def _zoidberg_object
|
@@ -244,6 +260,23 @@ module Zoidberg
|
|
244
260
|
_raw_instance.async(*args, &block)
|
245
261
|
end
|
246
262
|
|
263
|
+
# Initialize the signal instance if not
|
264
|
+
def _zoidberg_signal_interface
|
265
|
+
unless(@_zoidberg_signal)
|
266
|
+
@_zoidberg_signal = ::Zoidberg::Signal.new(:cache_signals => self.class.option?(:cache_signals))
|
267
|
+
end
|
268
|
+
@_zoidberg_signal
|
269
|
+
end
|
270
|
+
|
271
|
+
# @return [Timer]
|
272
|
+
def _zoidberg_timer
|
273
|
+
unless(@_zoidberg_timer)
|
274
|
+
@_zoidberg_timer = Timer.new
|
275
|
+
end
|
276
|
+
@_zoidberg_timer
|
277
|
+
end
|
278
|
+
|
279
|
+
|
247
280
|
end
|
248
281
|
end
|
249
282
|
|
@@ -60,7 +60,7 @@ module Zoidberg
|
|
60
60
|
else
|
61
61
|
res = _isolated_request(*args, &block)
|
62
62
|
end
|
63
|
-
rescue ::Zoidberg::
|
63
|
+
rescue ::Zoidberg::AbortException => e
|
64
64
|
::Kernel.raise e.original_exception
|
65
65
|
rescue ::Exception => e
|
66
66
|
_zoidberg_unexpected_error(e)
|
@@ -82,7 +82,7 @@ module Zoidberg
|
|
82
82
|
_raw_instance.__send__(method_name, *args, &block)
|
83
83
|
else
|
84
84
|
unless(_source_thread.alive?)
|
85
|
-
::Kernel.raise ::Zoidberg::DeadException.new('Instance in terminated state!')
|
85
|
+
::Kernel.raise ::Zoidberg::DeadException.new('Instance in terminated state!', object_id)
|
86
86
|
end
|
87
87
|
::Zoidberg.logger.debug "Received request from remote thread. Added to queue: #{_raw_instance.class}##{method_name}(#{args.map(&:inspect).join(', ')})"
|
88
88
|
response_queue = ::Queue.new
|
@@ -140,7 +140,7 @@ module Zoidberg
|
|
140
140
|
end
|
141
141
|
ensure
|
142
142
|
until(_requests.empty)
|
143
|
-
requests.pop[:response] << ::Zoidberg::DeadException.new('Instance in terminated state!')
|
143
|
+
requests.pop[:response] << ::Zoidberg::DeadException.new('Instance in terminated state!', object_id)
|
144
144
|
end
|
145
145
|
end
|
146
146
|
end
|
@@ -191,7 +191,7 @@ module Zoidberg
|
|
191
191
|
end
|
192
192
|
end
|
193
193
|
else
|
194
|
-
request[:response] << ::Zoidberg::DeadException.new('Instance in terminated state!')
|
194
|
+
request[:response] << ::Zoidberg::DeadException.new('Instance in terminated state!', object_id)
|
195
195
|
request[:task].halt!
|
196
196
|
end
|
197
197
|
end
|
@@ -5,6 +5,9 @@ module Zoidberg
|
|
5
5
|
|
6
6
|
class Liberated < Proxy
|
7
7
|
|
8
|
+
# Time allowed for threads to gracefully die
|
9
|
+
THREAD_KILL_AFTER = 5
|
10
|
+
|
8
11
|
# @return [Thread] current owner of lock
|
9
12
|
attr_reader :_locker
|
10
13
|
# @return [Hash<Integer:Thread>]
|
@@ -21,7 +24,7 @@ module Zoidberg
|
|
21
24
|
@_locker = nil
|
22
25
|
@_locker_count = 0
|
23
26
|
@_zoidberg_signal = nil
|
24
|
-
@_raw_threads =
|
27
|
+
@_raw_threads = []
|
25
28
|
@_supervised = klass.ancestors.include?(::Zoidberg::Supervise)
|
26
29
|
end
|
27
30
|
|
@@ -56,17 +59,12 @@ module Zoidberg
|
|
56
59
|
else
|
57
60
|
res = @_raw_instance.__send__(*args, &block)
|
58
61
|
end
|
59
|
-
rescue ::Zoidberg::
|
62
|
+
rescue ::Zoidberg::AbortException => e
|
60
63
|
::Kernel.raise e.original_exception
|
61
64
|
rescue ::Exception => e
|
62
65
|
::Zoidberg.logger.debug "Exception on: #{_raw_instance.class.name}##{args.first}(#{args.slice(1, args.size).map(&:inspect).join(', ')})"
|
63
66
|
_zoidberg_unexpected_error(e)
|
64
|
-
|
65
|
-
@_fatal_retry = true
|
66
|
-
retry
|
67
|
-
else
|
68
|
-
::Kernel.raise e
|
69
|
-
end
|
67
|
+
::Kernel.raise e
|
70
68
|
ensure
|
71
69
|
if(@got_lock)
|
72
70
|
_release_lock!
|
@@ -93,7 +91,7 @@ module Zoidberg
|
|
93
91
|
# @param thread [Thread]
|
94
92
|
# @return [TrueClass]
|
95
93
|
def _zoidberg_thread(thread)
|
96
|
-
_raw_threads
|
94
|
+
_raw_threads.push(thread)
|
97
95
|
true
|
98
96
|
end
|
99
97
|
|
@@ -140,23 +138,40 @@ module Zoidberg
|
|
140
138
|
#
|
141
139
|
# @return [TrueClass]
|
142
140
|
def _zoidberg_destroy!(error=nil)
|
141
|
+
object_string = self.inspect
|
142
|
+
oid = _raw_instance.object_id
|
143
|
+
::Zoidberg.logger.debug "*** Destroying zoidberg instance #{object_string}"
|
143
144
|
super do
|
144
|
-
_raw_threads
|
145
|
-
thread.raise ::Zoidberg::DeadException.new('Instance in terminated state!')
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
145
|
+
_raw_threads.map do |thread|
|
146
|
+
thread.raise ::Zoidberg::DeadException.new('Instance in terminated state!', oid)
|
147
|
+
::Thread.new(thread) do |thread|
|
148
|
+
next if thread == ::Thread.current
|
149
|
+
thread.join(::Zoidberg::Proxy::Liberated::THREAD_KILL_AFTER)
|
150
|
+
if(thread.alive?)
|
151
|
+
::Zoidberg.logger.error "Failed to halt async thread, killing: #{thread.inspect}"
|
152
|
+
thread.kill
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@_accessing_threads.each do |thread|
|
157
|
+
if(thread.alive?)
|
152
158
|
begin
|
153
|
-
|
159
|
+
thread.raise ::Zoidberg::DeadException.new('Instance in terminated state!', oid)
|
160
|
+
::Thread.new(thread) do |thread|
|
161
|
+
next if thread == ::Thread.current
|
162
|
+
thread.join(::Zoidberg::Proxy::Liberated::THREAD_KILL_AFTER)
|
163
|
+
if(thread.alive?)
|
164
|
+
::Zoidberg.logger.error "Failed to halt accessing thread, killing: #{thread.inspect}"
|
165
|
+
thread.kill
|
166
|
+
end
|
167
|
+
end
|
154
168
|
rescue
|
155
169
|
end
|
156
170
|
end
|
157
171
|
end
|
158
172
|
@_accessing_threads.clear
|
159
173
|
end
|
174
|
+
::Zoidberg.logger.debug "!!! Destroyed zoidberg instance #{object_string}"
|
160
175
|
end
|
161
176
|
|
162
177
|
end
|
data/lib/zoidberg/shell.rb
CHANGED
@@ -3,7 +3,26 @@ require 'zoidberg'
|
|
3
3
|
module Zoidberg
|
4
4
|
|
5
5
|
# Customized exception type used when instance has been terminated
|
6
|
-
class DeadException <
|
6
|
+
class DeadException < StandardError
|
7
|
+
attr_reader :origin_object_id
|
8
|
+
def initialize(message, origin_id=nil)
|
9
|
+
super message
|
10
|
+
@origin_object_id = origin_id
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Customized exception type to wrap allowed errors
|
15
|
+
class AbortException < StandardError
|
16
|
+
attr_accessor :original_exception
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
if(original_exception)
|
20
|
+
"#{original_exception.class}: #{original_exception}"
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
7
26
|
|
8
27
|
# Librated proxy based shell
|
9
28
|
module SoftShell
|
@@ -24,6 +43,11 @@ module Zoidberg
|
|
24
43
|
got_lock = locked
|
25
44
|
begin
|
26
45
|
target.send(*args, &block)
|
46
|
+
rescue Zoidberg::DeadException => e
|
47
|
+
if(e.origin_object_id == target.object_id)
|
48
|
+
got_lock = false
|
49
|
+
end
|
50
|
+
raise
|
27
51
|
rescue StandardError, ScriptError => e
|
28
52
|
origin_proxy._zoidberg_unexpected_error(e)
|
29
53
|
raise
|
@@ -46,6 +70,9 @@ module Zoidberg
|
|
46
70
|
begin
|
47
71
|
result = yield if block_given?
|
48
72
|
result
|
73
|
+
rescue ::Zoidberg::DeadException => e
|
74
|
+
re_lock = false if e.origin_object_id == object_id
|
75
|
+
raise
|
49
76
|
rescue ::StandardError, ::ScriptError => e
|
50
77
|
raise e
|
51
78
|
ensure
|
@@ -63,6 +90,13 @@ module Zoidberg
|
|
63
90
|
thread = ::Thread.new do
|
64
91
|
begin
|
65
92
|
self.instance_exec(&block)
|
93
|
+
rescue Zoidberg::DeadException => e
|
94
|
+
if(e.origin_object_id == object_id)
|
95
|
+
raise
|
96
|
+
else
|
97
|
+
_zoidberg_proxy._zoidberg_unexpected_error(e)
|
98
|
+
raise
|
99
|
+
end
|
66
100
|
rescue ::StandardError, ::ScriptError => e
|
67
101
|
_zoidberg_proxy._zoidberg_unexpected_error(e)
|
68
102
|
raise
|
@@ -74,6 +108,13 @@ module Zoidberg
|
|
74
108
|
begin
|
75
109
|
got_lock = true
|
76
110
|
self.instance_exec(&block)
|
111
|
+
rescue Zoidberg::DeadException => e
|
112
|
+
if(e.origin_object_id == object_id)
|
113
|
+
got_lock = false
|
114
|
+
else
|
115
|
+
_zoidberg_proxy._zoidberg_unexpected_error(e)
|
116
|
+
end
|
117
|
+
raise
|
77
118
|
rescue ::StandardError, ::ScriptError => e
|
78
119
|
_zoidberg_proxy._zoidberg_unexpected_error(e)
|
79
120
|
raise
|
@@ -219,18 +260,12 @@ module Zoidberg
|
|
219
260
|
|
220
261
|
# Initialize the signal instance if not
|
221
262
|
def _zoidberg_signal_interface
|
222
|
-
|
223
|
-
@_zoidberg_signal = ::Zoidberg::Signal.new(:cache_signals => self.class.option?(:cache_signals))
|
224
|
-
end
|
225
|
-
@_zoidberg_signal
|
263
|
+
_zoidberg_proxy._zoidberg_signal_interface
|
226
264
|
end
|
227
265
|
|
228
266
|
# @return [Timer]
|
229
267
|
def timer
|
230
|
-
|
231
|
-
@_zoidberg_timer = Timer.new
|
232
|
-
end
|
233
|
-
@_zoidberg_timer
|
268
|
+
_zoidberg_proxy._zoidberg_timer
|
234
269
|
end
|
235
270
|
|
236
271
|
# Register a recurring action
|
@@ -309,6 +344,21 @@ module Zoidberg
|
|
309
344
|
true
|
310
345
|
end
|
311
346
|
|
347
|
+
# Customized method for raising exceptions that have been
|
348
|
+
# properly handled (preventing termination)
|
349
|
+
#
|
350
|
+
# @param e [Exception]
|
351
|
+
# @raises [AbortException]
|
352
|
+
def abort(e)
|
353
|
+
unless(e.is_a?(::Exception))
|
354
|
+
$stdout.puts "E: #{e.class} - #{e.ancestors}"
|
355
|
+
e = StandardError.new(e)
|
356
|
+
end
|
357
|
+
new_e = ::Zoidberg::AbortException.new
|
358
|
+
new_e.original_exception = e
|
359
|
+
::Kernel.raise new_e
|
360
|
+
end
|
361
|
+
|
312
362
|
end
|
313
363
|
|
314
364
|
module ClassMethods
|
data/lib/zoidberg/signal.rb
CHANGED
@@ -80,6 +80,15 @@ module Zoidberg
|
|
80
80
|
val == EMPTY_VALUE ? (Time.now.to_f - start_sleep) : val
|
81
81
|
end
|
82
82
|
|
83
|
+
# Force any waiter threads into an error state
|
84
|
+
def terminate
|
85
|
+
waiters.each do |k,v|
|
86
|
+
v[:threads].each do |thread|
|
87
|
+
thread.raise ::Zoidberg::DeadException.new('Instance in terminated state!', object_id)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
83
92
|
protected
|
84
93
|
|
85
94
|
# Initialize the signal structure data
|
data/lib/zoidberg/supervise.rb
CHANGED
@@ -3,36 +3,6 @@ require 'zoidberg'
|
|
3
3
|
module Zoidberg
|
4
4
|
# Add supervision to instance
|
5
5
|
module Supervise
|
6
|
-
# Customized exception type to wrap allowed errors
|
7
|
-
class AbortException < StandardError
|
8
|
-
attr_accessor :original_exception
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
if(original_exception)
|
12
|
-
"#{original_exception.class}: #{original_exception}"
|
13
|
-
else
|
14
|
-
super
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module InstanceMethods
|
20
|
-
|
21
|
-
# Customized method for raising exceptions that have been
|
22
|
-
# properly handled (preventing termination)
|
23
|
-
#
|
24
|
-
# @param e [Exception]
|
25
|
-
# @raises [AbortException]
|
26
|
-
def abort(e)
|
27
|
-
unless(e.is_a?(::Exception))
|
28
|
-
e = StandardError.new(e)
|
29
|
-
end
|
30
|
-
new_e = ::Zoidberg::Supervise::AbortException.new
|
31
|
-
new_e.original_exception = e
|
32
|
-
::Kernel.raise new_e
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
6
|
|
37
7
|
# Include supervision into given class when included
|
38
8
|
#
|
@@ -41,11 +11,6 @@ module Zoidberg
|
|
41
11
|
unless(klass.include?(Zoidberg::Shell))
|
42
12
|
klass.class_eval{ include Zoidberg::Shell }
|
43
13
|
end
|
44
|
-
unless(klass.include?(Zoidberg::Supervise::InstanceMethods))
|
45
|
-
klass.class_eval do
|
46
|
-
include InstanceMethods
|
47
|
-
end
|
48
|
-
end
|
49
14
|
end
|
50
15
|
|
51
16
|
end
|
data/lib/zoidberg/timer.rb
CHANGED
@@ -70,9 +70,6 @@ module Zoidberg
|
|
70
70
|
|
71
71
|
include Zoidberg::SoftShell
|
72
72
|
|
73
|
-
# Custom exception used to wakeup timer
|
74
|
-
class Wakeup < StandardError; end
|
75
|
-
|
76
73
|
# Wakeup string
|
77
74
|
WAKEUP = "WAKEUP\n"
|
78
75
|
|
@@ -204,6 +201,11 @@ module Zoidberg
|
|
204
201
|
current_self
|
205
202
|
end
|
206
203
|
|
204
|
+
# Clean up timer thread
|
205
|
+
def terminate
|
206
|
+
@thread.raise Zoidberg::DeadException.new('Instance in terminated state', object_id)
|
207
|
+
end
|
208
|
+
|
207
209
|
protected
|
208
210
|
|
209
211
|
# Run the timer loop
|
@@ -218,8 +220,13 @@ module Zoidberg
|
|
218
220
|
begin
|
219
221
|
run_ready
|
220
222
|
reset(false)
|
221
|
-
rescue DeadException
|
222
|
-
|
223
|
+
rescue DeadException => e
|
224
|
+
if(e.origin_object_id == object_id)
|
225
|
+
Zoidberg.logger.debug "<#{self}> Terminated state encountered. Falling out of run loop!"
|
226
|
+
raise
|
227
|
+
else
|
228
|
+
current_self.raise e
|
229
|
+
end
|
223
230
|
rescue => e
|
224
231
|
Zoidberg.logger.error "<#{self}> Unexpected error in runner: #{e.class.name} - #{e}"
|
225
232
|
Zoidberg.logger.debug "<#{self}> #{e.class.name}: #{e}\n#{e.backtrace.join("\n")}"
|
data/lib/zoidberg/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zoidberg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Roberts
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bogo
|