zoidberg 0.1.12 → 0.2.0
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.
- 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
|