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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99176e505b497b0889a91f059c260a8b1ef45645
4
- data.tar.gz: 52260a78198f95b9babc69b92af2ac00e43e9769
3
+ metadata.gz: 8f631edac70299825464b22bcb61b054248eb0da
4
+ data.tar.gz: 841685d0521b5b932c542db06d6d922a74d6c5ed
5
5
  SHA512:
6
- metadata.gz: 6823ccd0afece4a93c2b4533dc301ffe75bc8344d4bba9409428cfedd8d10d5930f11a67c119accc05f4384cee6ae56b85d4891b938011c87f781acff22e971c
7
- data.tar.gz: 7fd4eb56fbdf12be7e007f7fabf1cba8e6b8c850b3de906432330d89f559738c2a9d2d0b7038f3024d55fe4e4e1cc57515b69c1fa20adfbdd3ae3f1b145d1f9e
6
+ metadata.gz: c4da51a62307d58f60fb8947337f41e45786bcd3b364b6ada62c6f5f2c3dc225afd1193b8f020d4dcc00da6b9eab459520182ff4b6818993a753b67536c4c543
7
+ data.tar.gz: 76b9c10d8f7896f66eb5640e7218bc84779e7df993dccb2807832b874729150c2e1979ca78601d9ad1d91429b2420839276c5c58716fd55011738d694018128a
@@ -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.
@@ -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
 
@@ -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
- worker.send(*args, &block)
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
@@ -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
- unless((defined?(Timeout) && e.is_a?(Timeout::Error)) || e.is_a?(::Zoidberg::DeadException))
128
- if(_zoidberg_link)
129
- if(_zoidberg_link.class.trap_exit)
130
- ::Zoidberg.logger.warn "Calling linked exit trapper #{@_raw_instance.class.name} -> #{_zoidberg_link.class}: #{e.class} - #{e}"
131
- _zoidberg_link.async.send(
132
- _zoidberg_link.class.trap_exit, @_raw_instance, e
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
- unless(::Zoidberg.in_shutdown?)
160
- if(_raw_instance.respond_to?(:restart))
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
- _zoidberg_destroy!
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
- if(_raw_instance.method(:terminate).arity == 0)
193
- _raw_instance.terminate
194
- else
195
- _raw_instance.terminate(error)
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
- alias_method :terminate, :_zoidberg_destroy!
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::Supervise::AbortException => e
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 = ::Smash.new{ ::Array.new }
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::Supervise::AbortException => e
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
- if(e.class.to_s == 'fatal' && !@_fatal_retry)
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[self.object_id].push(thread)
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[_raw_instance.object_id].map do |thread|
145
- thread.raise ::Zoidberg::DeadException.new('Instance in terminated state!')
146
- end.map do |thread|
147
- thread.join(2)
148
- end.find_all(&:alive?).map(&:kill)
149
- _raw_threads.delete(_raw_instance.object_id)
150
- @_accessing_threads.each do |thr|
151
- if(thr.alive?)
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
- thr.raise ::Zoidberg::DeadException.new('Instance in terminated state!')
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
@@ -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 < RuntimeError; end
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
- unless(@_zoidberg_signal)
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
- unless(@_zoidberg_timer)
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
@@ -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
@@ -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
@@ -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
- raise
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")}"
@@ -1,4 +1,4 @@
1
1
  module Zoidberg
2
2
  # Current library version
3
- VERSION = Gem::Version.new('0.1.12')
3
+ VERSION = Gem::Version.new('0.2.0')
4
4
  end
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.1.12
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-13 00:00:00.000000000 Z
11
+ date: 2015-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bogo