zoidberg 0.1.4 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3632fd491e21d5b71dca06d2670227b281b27865
4
- data.tar.gz: 360be229f5e280316a28b6505b80e08fba50220b
3
+ metadata.gz: 08a8fcac4a08bd3faf0b9df558810aff252ad731
4
+ data.tar.gz: 592ee902246aa7894b9a06e3fe5a1a5004cd79cf
5
5
  SHA512:
6
- metadata.gz: f3d3c32e84acbd7b470f16166b070b8bf219d3db1d548eeb25bb758e9675c1b8b864ec45cbe5b4536b402147ce6b0513cefbb5af2008855eea4af48a21c29366
7
- data.tar.gz: 3bd1a5562bdab8f04ac8f0e1381b1db7aa10c33cb0e1f83e334802cf348b329dae5e6091052b875efab3b3c740371620147c679dcbdb72962f5cb690321fa15c
6
+ metadata.gz: 30c31fccb644651b16bcb4800950abaad291a1c889e0610e35eda0544478b9b36d4395ded8689965ad6f27b25b3948e690bd051e7b4edb5f86013779595dd5e4
7
+ data.tar.gz: da72c8a5e1e394cd6acdd2898aa7e647da58478d5408c6b4f2c741fac92705fc1efe641d8cb729f2913f69bdbfcf66d8d30c9cebafd2f7b99e77eccab9a61cc9
@@ -1,3 +1,9 @@
1
+ # v0.1.6
2
+ * Add Zoidberg::Lazy
3
+ * Explicitly state what shells are used internally
4
+ * DRY raw instance wrapping
5
+ * Flag signal context and disable any locking
6
+
1
7
  # v0.1.4
2
8
  * Timer updates and fix spec
3
9
 
@@ -7,6 +7,7 @@ require 'zoidberg/version'
7
7
  module Zoidberg
8
8
  autoload :DeadException, 'zoidberg/shell'
9
9
  autoload :Future, 'zoidberg/future'
10
+ autoload :Lazy, 'zoidberg/lazy'
10
11
  autoload :Logger, 'zoidberg/logger'
11
12
  autoload :Pool, 'zoidberg/pool'
12
13
  autoload :Proxy, 'zoidberg/proxy'
@@ -23,6 +24,7 @@ module Zoidberg
23
24
 
24
25
  class << self
25
26
 
27
+ attr_accessor :signal_shutdown
26
28
  attr_accessor :default_shell
27
29
 
28
30
  # @return [Zoidberg::Logger]
@@ -46,6 +48,14 @@ module Zoidberg
46
48
  SecureRandom.uuid
47
49
  end
48
50
 
51
+ def signal_reset
52
+ self.signal_shutdown = false
53
+ end
54
+
55
+ def in_shutdown?
56
+ !!self.signal_shutdown
57
+ end
58
+
49
59
  end
50
60
 
51
61
  end
@@ -54,3 +64,9 @@ end
54
64
  Zoidberg.logger = Zoidberg::Logger.new(STDERR)
55
65
  # Set default shell to soft shell
56
66
  Zoidberg.default_shell = Zoidberg::SoftShell
67
+
68
+ %w(INT TERM).each do |sig_name|
69
+ Signal.trap(sig_name) do |*_|
70
+ Zoidberg.signal_shutdown = true
71
+ end
72
+ end
@@ -0,0 +1,56 @@
1
+ require 'zoidberg'
2
+
3
+ module Zoidberg
4
+
5
+ # Provide lazy evaluation of an instance for future work
6
+ class Lazy < BasicObject
7
+
8
+ # Create a new lazy evaluator
9
+ #
10
+ # @param klass [Class] optional class to use for type checkin
11
+ # @yield block to provide actual instance
12
+ # @return [self]
13
+ def initialize(klass=nil, &block)
14
+ @klass = klass
15
+ @lock = ::Mutex.new
16
+ unless(block)
17
+ ::Kernel.raise ::ArgumentError.new('Block is required for providing instance!')
18
+ end
19
+ @instance_block = block
20
+ end
21
+
22
+ # Proxy any calls to actual instance
23
+ def method_missing(*args, &block)
24
+ _lazy_instance.send(*args, &block)
25
+ end
26
+
27
+ # Customized check to allow for type checking prior to instance
28
+ # being available via lazy loading
29
+ #
30
+ # @param chk [Class]
31
+ # @return [TrueClass, FalseClass]
32
+ def is_a?(chk)
33
+ if(@klass)
34
+ ([@klass] + @klass.ancestors).include?(chk)
35
+ else
36
+ method_missing(:is_a?, chk)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # @return [Object]
43
+ def _lazy_instance
44
+ @lock.synchronize do
45
+ unless(@instance)
46
+ until(@instance = @instance_block.call)
47
+ ::Kernel.sleep(0.1)
48
+ end
49
+ end
50
+ @instance
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -5,7 +5,7 @@ module Zoidberg
5
5
  # instances within the pool
6
6
  class Pool
7
7
 
8
- include Zoidberg::Shell
8
+ include Zoidberg::SoftShell
9
9
 
10
10
  # @return [Array<Object>] workers within pool
11
11
  attr_reader :_workers
@@ -53,11 +53,31 @@ module Zoidberg
53
53
  raise NotImplementedError
54
54
  end
55
55
 
56
+ # Set the raw instance into the proxy and link proxy to instance
57
+ #
58
+ # @param inst [Object] raw instance being wrapped
59
+ # @return [NilClass]
60
+ def _zoidberg_set_instance(inst)
61
+ @_raw_instance = inst
62
+ @_raw_instance._zoidberg_proxy(self)
63
+ nil
64
+ end
65
+
56
66
  # @return [TrueClass, FalseClass] currently locked
57
67
  def _zoidberg_locked?
58
68
  false
59
69
  end
60
70
 
71
+ # @return [TrueClass]
72
+ def _aquire_lock!
73
+ true
74
+ end
75
+
76
+ # @return [TrueClass]
77
+ def _release_lock!
78
+ true
79
+ end
80
+
61
81
  # @return [TrueClass, FalseClass] currently unlocked
62
82
  def _zoidberg_available?
63
83
  !_zoidberg_locked?
@@ -99,6 +119,7 @@ module Zoidberg
99
119
  # @param e [Exception]
100
120
  def _zoidberg_unexpected_error(e)
101
121
  ::Zoidberg.logger.error "Unexpected exception: #{e.class} - #{e}"
122
+ ::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
102
123
  unless((defined?(Timeout) && e.is_a?(Timeout::Error)) || e.is_a?(::Zoidberg::DeadException))
103
124
  if(_zoidberg_link)
104
125
  if(_zoidberg_link.class.trap_exit)
@@ -131,25 +152,24 @@ module Zoidberg
131
152
  # @param error [Exception] exception that was caught
132
153
  # @return [TrueClass]
133
154
  def _zoidberg_handle_unexpected_error(error)
134
- if(_raw_instance.respond_to?(:restart))
135
- begin
136
- _raw_instance.restart(error)
137
- return # short circuit
138
- rescue => e
155
+ unless(::Zoidberg.in_shutdown?)
156
+ if(_raw_instance.respond_to?(:restart))
157
+ begin
158
+ _raw_instance.restart(error)
159
+ return # short circuit
160
+ rescue => e
161
+ end
139
162
  end
163
+ _zoidberg_destroy!
164
+ _aquire_lock!
165
+ args = _build_args.dup
166
+ inst = args.shift.unshelled_new(*args.first, &args.last)
167
+ _zoidberg_set_instance(inst)
168
+ if(_raw_instance.respond_to?(:restarted!))
169
+ _raw_instance.restarted!
170
+ end
171
+ _release_lock!
140
172
  end
141
- _zoidberg_destroy!
142
- _aquire_lock!
143
- args = _build_args.dup
144
- @_raw_instance = args.shift.unshelled_new(
145
- *args.first,
146
- &args.last
147
- )
148
- _raw_instance._zoidberg_proxy(self)
149
- if(_raw_instance.respond_to?(:restarted!))
150
- _raw_instance.restarted!
151
- end
152
- _release_lock!
153
173
  true
154
174
  end
155
175
 
@@ -189,7 +209,6 @@ module Zoidberg
189
209
  _raw_instance.send(:define_singleton_method, :to_s, &death_from_above_display)
190
210
  _raw_instance.send(:define_singleton_method, :inspect, &death_from_above_display)
191
211
  _raw_instance.send(:define_singleton_method, :_zoidberg_destroyed, ::Proc.new{ true })
192
- _zoidberg_signal(:destroyed)
193
212
  end
194
213
  true
195
214
  end
@@ -9,6 +9,8 @@ module Zoidberg
9
9
  attr_reader :_source_thread
10
10
  # @return [Queue] current request queue
11
11
  attr_reader :_requests
12
+ # @return [Queue] result queue
13
+ attr_reader :_results
12
14
  # @return [TrueClass, FalseClass] blocked running task
13
15
  attr_reader :_blocked
14
16
 
@@ -18,6 +20,7 @@ module Zoidberg
18
20
  # @return [self]
19
21
  def initialize(klass, *args, &block)
20
22
  @_requests = ::Queue.new
23
+ @_results = ::Queue.new
21
24
  @_blocked = false
22
25
  @_source_thread = ::Thread.new do
23
26
  ::Zoidberg.logger.debug 'Starting the isolation request processor'
@@ -25,11 +28,7 @@ module Zoidberg
25
28
  _isolate!
26
29
  end
27
30
  @_build_args = [klass, *args, block]
28
- @_raw_instance = klass.unshelled_new(*args, &block)
29
- @_raw_instance._zoidberg_proxy(self)
30
- if(@_raw_instance.class.include?(::Zoidberg::Supervise))
31
- @_supervised = true
32
- end
31
+ @_supervised = klass.include?(::Zoidberg::Supervise)
33
32
  ::Zoidberg.logger.debug "Zoidberg object isolation wrap: #{@_build_args.inspect}"
34
33
  end
35
34
 
@@ -44,7 +43,7 @@ module Zoidberg
44
43
  :block => block,
45
44
  :response => nil,
46
45
  :async => true,
47
- :blocking => blocking == :blocking
46
+ :blocked => !!blocking
48
47
  )
49
48
  nil
50
49
  end
@@ -102,22 +101,41 @@ module Zoidberg
102
101
  end
103
102
  end
104
103
 
104
+ # @return [TrueClass, FalseClass]
105
105
  def _zoidberg_available?
106
106
  !_blocked
107
107
  end
108
108
 
109
+ # @return [TrueClass, FalseClass]
110
+ def threaded?
111
+ ::Thread.current != _source_thread
112
+ end
113
+
114
+ def task_defer(wait_task)
115
+ _results << wait_task
116
+ end
117
+
118
+ def inspect
119
+ _raw_instance.inspect
120
+ end
121
+
122
+ def to_s
123
+ _raw_inspect.to_s
124
+ end
125
+
109
126
  protected
110
127
 
111
128
  # Process requests
112
129
  def _isolate!
113
130
  begin
114
131
  ::Kernel.loop do
132
+ item = _requests.pop
115
133
  begin
116
- _process_request(_requests.pop)
134
+ _process_request(item)
117
135
  rescue => e
118
136
  ::Zoidberg.logger.error "Unexpected looping error! (#{e.class}: #{e})"
119
- ::Zoidberg.logger.error "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
120
- ::Thread.main.raise e
137
+ ::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
138
+ item.push(e)
121
139
  end
122
140
  end
123
141
  ensure
@@ -130,43 +148,57 @@ module Zoidberg
130
148
  # Process a request
131
149
  #
132
150
  # @param request [Hash]
133
- # @return [self]
151
+ # @return [NilClass]
134
152
  def _process_request(request)
135
153
  begin
136
154
  @_blocked = !request[:async]
137
155
  ::Zoidberg.logger.debug "Processing received request: #{request.inspect}"
138
156
  unless(request[:task])
139
- request[:task] = ::Zoidberg::Task.new(request[:async] ? :async : :serial, _raw_instance, [request]) do |req|
157
+ if(request[:response])
158
+ request[:result] = _results
159
+ end
160
+ request[:task] = ::Zoidberg::Task.new(!self.class.option?(:evented) || request[:async] ? :async : :serial, _raw_instance, [request]) do |req|
140
161
  begin
141
162
  result = origin.__send__(
142
163
  *req[:arguments],
143
164
  &req[:block]
144
165
  )
145
166
  if(req[:response])
146
- req[:response] << result
167
+ req[:result] << result
147
168
  end
148
169
  rescue ::Exception => exception
149
170
  if(req[:response])
150
- req[:response] << exception
171
+ req[:result] << exception
151
172
  end
152
173
  end
153
174
  end
154
175
  end
155
- if(request[:task].waiting?)
176
+ if(!request[:task_defer] || request[:task_defer].complete?)
156
177
  if(_raw_instance.alive?)
157
- request[:task].proceed
158
- request[:task].value if request[:blocking]
178
+ if(request[:task].waiting?)
179
+ request[:task].proceed
180
+ end
181
+ if(!request[:async] || request[:blocking])
182
+ val = _results.pop
183
+ if(val.is_a?(::Zoidberg::Task))
184
+ ::Thread.new do
185
+ val.value
186
+ _requests.push(request)
187
+ end
188
+ else
189
+ request[:response].push(val)
190
+ ::Zoidberg.logger.debug "Request processing completed. #{request.inspect}"
191
+ end
192
+ end
159
193
  else
160
194
  request[:response] << ::Zoidberg::DeadException.new('Instance in terminated state!')
161
195
  request[:task].halt!
162
196
  end
163
197
  end
164
- _requests.push(request) unless request[:task].complete? || request[:async]
165
- ::Zoidberg.logger.debug "Request processing completed. #{request.inspect}"
166
198
  ensure
167
199
  @_blocked = false
168
200
  end
169
- self
201
+ nil
170
202
  end
171
203
 
172
204
  end
@@ -21,12 +21,8 @@ module Zoidberg
21
21
  @_locker = nil
22
22
  @_locker_count = 0
23
23
  @_zoidberg_signal = nil
24
- @_raw_instance = klass.unshelled_new(*args, &block)
25
- @_raw_instance._zoidberg_proxy(self)
26
24
  @_raw_threads = ::Smash.new{ ::Array.new }
27
- if(@_raw_instance.class.ancestors.include?(::Zoidberg::Supervise))
28
- @_supervised = true
29
- end
25
+ @_supervised = klass.ancestors.include?(::Zoidberg::Supervise)
30
26
  end
31
27
 
32
28
  # Used to proxy request to real instance
@@ -2,6 +2,6 @@ require 'zoidberg'
2
2
 
3
3
  module Zoidberg
4
4
  class Registry < Bogo::Smash
5
- include Zoidberg::Shell
5
+ include Zoidberg::SoftShell
6
6
  end
7
7
  end
@@ -132,9 +132,16 @@ module Zoidberg
132
132
  # @yield block to execute without lock
133
133
  # @return [Object] result of block
134
134
  def defer(&block)
135
- Fiber.yield
136
- if(block)
137
- ::Fiber.new(&block).resume
135
+ if(current_self.threaded?)
136
+ action = Task.new(:async, current_self){ block.call }
137
+ current_self.task_defer(action)
138
+ Thread.stop
139
+ action.value
140
+ else
141
+ Fiber.yield
142
+ if(block)
143
+ ::Fiber.new(&block).resume
144
+ end
138
145
  end
139
146
  end
140
147
 
@@ -162,7 +169,7 @@ module Zoidberg
162
169
  def sleep(length=nil)
163
170
  start_time = ::Time.now.to_f
164
171
  if(length)
165
- ::Kernel.sleep(length)
172
+ defer{ ::Kernel.sleep(length) }
166
173
  else
167
174
  ::Thread.current[:root_fiber] == ::Fiber.current ? ::Kernel.sleep : ::Fiber.yield
168
175
  end
@@ -258,7 +265,11 @@ module Zoidberg
258
265
  if(oxy)
259
266
  @_zoidberg_proxy = oxy
260
267
  end
261
- @_zoidberg_proxy
268
+ if(@_zoidberg_proxy)
269
+ @_zoidberg_proxy
270
+ else
271
+ Lazy.new(self.class){ @_zoidberg_proxy }
272
+ end
262
273
  end
263
274
  alias_method :current_self, :_zoidberg_proxy
264
275
  alias_method :current_actor, :_zoidberg_proxy
@@ -286,6 +297,7 @@ module Zoidberg
286
297
  else
287
298
  raise TypeError.new "Unable to determine `Shell` type for this class `#{self}`!"
288
299
  end
300
+ proxy._zoidberg_set_instance(self.unshelled_new(*args, &block))
289
301
  weak_ref = Zoidberg::WeakRef.new(proxy)
290
302
  Zoidberg::Proxy.register(weak_ref.__id__, proxy)
291
303
  ObjectSpace.define_finalizer(weak_ref, Zoidberg::Proxy.method(:scrub!))
@@ -3,7 +3,7 @@ require 'zoidberg'
3
3
  module Zoidberg
4
4
  class Supervisor
5
5
 
6
- include Zoidberg::Shell
6
+ include Zoidberg::SoftShell
7
7
 
8
8
  # @return [Registry] current supervision registry
9
9
  attr_reader :registry
@@ -140,6 +140,10 @@ module Zoidberg
140
140
  raise
141
141
  end
142
142
  end
143
+ until(@task.stop?)
144
+ sleep(0.01)
145
+ end
146
+ @task
143
147
  end
144
148
 
145
149
  end
@@ -1,4 +1,4 @@
1
1
  module Zoidberg
2
2
  # Current library version
3
- VERSION = Gem::Version.new('0.1.4')
3
+ VERSION = Gem::Version.new('0.1.6')
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.4
4
+ version: 0.1.6
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-09-17 00:00:00.000000000 Z
11
+ date: 2015-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bogo
@@ -78,6 +78,7 @@ files:
78
78
  - README.md
79
79
  - lib/zoidberg.rb
80
80
  - lib/zoidberg/future.rb
81
+ - lib/zoidberg/lazy.rb
81
82
  - lib/zoidberg/logger.rb
82
83
  - lib/zoidberg/pool.rb
83
84
  - lib/zoidberg/proxy.rb
@@ -113,9 +114,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
114
  version: '0'
114
115
  requirements: []
115
116
  rubyforge_project:
116
- rubygems_version: 2.2.2
117
+ rubygems_version: 2.4.8
117
118
  signing_key:
118
119
  specification_version: 4
119
120
  summary: Why not?
120
121
  test_files: []
121
- has_rdoc: