einhorn 0.7.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/Changes.md +10 -0
  3. data/{LICENSE → LICENSE.txt} +0 -0
  4. data/README.md +36 -30
  5. data/bin/einhorn +17 -2
  6. data/einhorn.gemspec +23 -21
  7. data/example/pool_worker.rb +1 -1
  8. data/example/thin_example +8 -8
  9. data/example/time_server +5 -5
  10. data/lib/einhorn/client.rb +8 -9
  11. data/lib/einhorn/command/interface.rb +100 -95
  12. data/lib/einhorn/command.rb +170 -88
  13. data/lib/einhorn/compat.rb +7 -7
  14. data/lib/einhorn/event/abstract_text_descriptor.rb +31 -35
  15. data/lib/einhorn/event/ack_timer.rb +2 -2
  16. data/lib/einhorn/event/command_server.rb +7 -9
  17. data/lib/einhorn/event/connection.rb +1 -3
  18. data/lib/einhorn/event/loop_breaker.rb +2 -1
  19. data/lib/einhorn/event/persistent.rb +2 -2
  20. data/lib/einhorn/event/timer.rb +4 -4
  21. data/lib/einhorn/event.rb +29 -20
  22. data/lib/einhorn/prctl.rb +26 -0
  23. data/lib/einhorn/prctl_linux.rb +48 -0
  24. data/lib/einhorn/safe_yaml.rb +17 -0
  25. data/lib/einhorn/version.rb +1 -1
  26. data/lib/einhorn/worker.rb +67 -49
  27. data/lib/einhorn/worker_pool.rb +9 -9
  28. data/lib/einhorn.rb +164 -155
  29. metadata +42 -137
  30. data/.gitignore +0 -17
  31. data/.travis.yml +0 -10
  32. data/CONTRIBUTORS +0 -6
  33. data/Gemfile +0 -11
  34. data/History.txt +0 -4
  35. data/README.md.in +0 -76
  36. data/Rakefile +0 -27
  37. data/test/_lib.rb +0 -12
  38. data/test/integration/_lib/fixtures/env_printer/env_printer.rb +0 -26
  39. data/test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb +0 -22
  40. data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
  41. data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -22
  42. data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -143
  43. data/test/integration/_lib/helpers.rb +0 -4
  44. data/test/integration/_lib.rb +0 -6
  45. data/test/integration/startup.rb +0 -31
  46. data/test/integration/upgrading.rb +0 -157
  47. data/test/unit/einhorn/client.rb +0 -88
  48. data/test/unit/einhorn/command/interface.rb +0 -49
  49. data/test/unit/einhorn/command.rb +0 -21
  50. data/test/unit/einhorn/event.rb +0 -89
  51. data/test/unit/einhorn/worker_pool.rb +0 -39
  52. data/test/unit/einhorn.rb +0 -58
@@ -7,13 +7,13 @@ module Einhorn
7
7
  end
8
8
 
9
9
  def self.workers
10
- workers_with_state.map {|pid, _| pid}
10
+ workers_with_state.map { |pid, _| pid }
11
11
  end
12
12
 
13
13
  def self.unsignaled_workers
14
14
  workers_with_state.select do |pid, spec|
15
15
  spec[:signaled].length == 0
16
- end.map {|pid, _| pid}
16
+ end.map { |pid, _| pid }
17
17
  end
18
18
 
19
19
  def self.modern_workers_with_state
@@ -23,15 +23,15 @@ module Einhorn
23
23
  end
24
24
 
25
25
  def self.acked_modern_workers_with_state
26
- modern_workers_with_state.select {|pid, spec| spec[:acked]}
26
+ modern_workers_with_state.select { |pid, spec| spec[:acked] }
27
27
  end
28
28
 
29
29
  def self.modern_workers
30
- modern_workers_with_state.map {|pid, _| pid}
30
+ modern_workers_with_state.map { |pid, _| pid }
31
31
  end
32
32
 
33
33
  def self.acked_modern_workers
34
- acked_modern_workers_with_state.map {|pid, _| pid}
34
+ acked_modern_workers_with_state.map { |pid, _| pid }
35
35
  end
36
36
 
37
37
  def self.unsignaled_modern_workers_with_state
@@ -43,23 +43,23 @@ module Einhorn
43
43
  def self.acked_unsignaled_modern_workers
44
44
  acked_modern_workers_with_state.select do |_, spec|
45
45
  spec[:signaled].length == 0
46
- end.map {|pid, _| pid}
46
+ end.map { |pid, _| pid }
47
47
  end
48
48
 
49
49
  def self.unsignaled_modern_workers_with_priority
50
50
  unsignaled_modern_workers_with_state.sort_by do |pid, spec|
51
51
  spec[:acked] ? 1 : 0
52
- end.map {|pid, _| pid}
52
+ end.map { |pid, _| pid }
53
53
  end
54
54
 
55
55
  def self.unacked_unsignaled_modern_workers_with_state
56
- modern_workers_with_state.select {|pid, spec|
56
+ modern_workers_with_state.select { |pid, spec|
57
57
  !spec[:acked] && spec[:signaled].length == 0
58
58
  }
59
59
  end
60
60
 
61
61
  def self.unacked_unsignaled_modern_workers
62
- unacked_unsignaled_modern_workers_with_state.map {|pid, _| pid}
62
+ unacked_unsignaled_modern_workers_with_state.map { |pid, _| pid }
63
63
  end
64
64
 
65
65
  # Use the number of modern workers, rather than unsignaled modern
data/lib/einhorn.rb CHANGED
@@ -1,18 +1,36 @@
1
- require 'fcntl'
2
- require 'optparse'
3
- require 'pp'
4
- require 'set'
5
- require 'socket'
6
- require 'tmpdir'
7
- require 'yaml'
8
- require 'shellwords'
1
+ require "fcntl"
2
+ require "optparse"
3
+ require "pp"
4
+ require "set"
5
+ require "socket"
6
+ require "tmpdir"
7
+ require "yaml"
8
+ require "shellwords"
9
+ require "einhorn/safe_yaml"
9
10
 
10
11
  module Einhorn
11
12
  module AbstractState
12
- def default_state; raise NotImplementedError.new('Override in extended modules'); end
13
- def state; @state ||= default_state; end
14
- def state=(v); @state = v; end
15
- def dumpable_state; state; end
13
+ def default_state
14
+ raise NotImplementedError.new("Override in extended modules")
15
+ end
16
+
17
+ def state
18
+ @state ||= default_state
19
+ end
20
+
21
+ def state=(v)
22
+ @state = v
23
+ end
24
+
25
+ def dumpable_state
26
+ state
27
+ end
28
+
29
+ def respond_to_missing?(name)
30
+ ((name.to_s =~ /(.*)=$/) && state.has_key?($1.to_sym)) ||
31
+ state.has_key?(name) ||
32
+ default_state.has_key?(name)
33
+ end
16
34
 
17
35
  def method_missing(name, *args)
18
36
  if (name.to_s =~ /(.*)=$/) && state.has_key?($1.to_sym)
@@ -37,66 +55,61 @@ module Einhorn
37
55
  # about backwards/forwards compatibility for upgrades/downgrades
38
56
  def self.default_state
39
57
  {
40
- :children => {},
41
- :config => {:number => 1, :backlog => 100, :seconds => 1},
42
- :versions => {},
43
- :version => 0,
44
- :sockets => {},
45
- :orig_cmd => nil,
46
- :bind => [],
47
- :bind_fds => [],
48
- :cmd => nil,
49
- :script_name => nil,
50
- :respawn => true,
51
- :upgrading => false,
52
- :smooth_upgrade => false,
53
- :reloading_for_upgrade => false,
54
- :path => nil,
55
- :cmd_name => nil,
56
- :verbosity => 1,
57
- :generation => 0,
58
- :last_spinup => nil,
59
- :ack_mode => {:type => :timer, :timeout => 1},
60
- :kill_children_on_exit => false,
61
- :command_socket_as_fd => false,
62
- :socket_path => nil,
63
- :pidfile => nil,
64
- :lockfile => nil,
65
- :consecutive_deaths_before_ack => 0,
66
- :last_upgraded => nil,
67
- :nice => {:master => nil, :worker => nil, :renice_cmd => '/usr/bin/renice'},
68
- :reexec_commandline => nil,
69
- :drop_environment_variables => [],
70
- :signal_timeout => nil,
58
+ children: {},
59
+ config: {number: 1, backlog: 100, seconds: 1},
60
+ versions: {},
61
+ version: 0,
62
+ sockets: {},
63
+ orig_cmd: nil,
64
+ bind: [],
65
+ bind_fds: [],
66
+ bound_ports: [],
67
+ cmd: nil,
68
+ script_name: nil,
69
+ respawn: true,
70
+ upgrading: false,
71
+ smooth_upgrade: false,
72
+ reloading_for_upgrade: false,
73
+ path: nil,
74
+ cmd_name: nil,
75
+ verbosity: 1,
76
+ generation: 0,
77
+ last_spinup: nil,
78
+ ack_mode: {type: :timer, timeout: 1},
79
+ kill_children_on_exit: false,
80
+ command_socket_as_fd: false,
81
+ socket_path: nil,
82
+ pidfile: nil,
83
+ lockfile: nil,
84
+ consecutive_deaths_before_ack: 0,
85
+ last_upgraded: nil,
86
+ nice: {master: nil, worker: nil, renice_cmd: "/usr/bin/renice"},
87
+ reexec_commandline: nil,
88
+ drop_environment_variables: [],
89
+ signal_timeout: nil,
90
+ preloaded: false
71
91
  }
72
92
  end
73
-
74
- def self.dumpable_state
75
- dump = state
76
- dump[:reloading_for_preload_upgrade] = dump[:reloading_for_upgrade]
77
- dump
78
- end
79
93
  end
80
94
 
81
95
  module TransientState
82
96
  extend AbstractState
83
97
  def self.default_state
84
98
  {
85
- :whatami => :master,
86
- :preloaded => false,
87
- :script_name => nil,
88
- :argv => [],
89
- :environ => {},
90
- :has_outstanding_spinup_timer => false,
91
- :stateful => nil,
99
+ whatami: :master,
100
+ script_name: nil,
101
+ argv: [],
102
+ environ: {},
103
+ has_outstanding_spinup_timer: false,
104
+ stateful: nil,
92
105
  # Holds references so that the GC doesn't go and close your sockets.
93
- :socket_handles => Set.new
106
+ socket_handles: Set.new
94
107
  }
95
108
  end
96
109
  end
97
110
 
98
111
  def self.restore_state(state)
99
- parsed = YAML.load(state)
112
+ parsed = SafeYAML.load(state)
100
113
  updated_state, message = update_state(Einhorn::State, "einhorn", parsed[:state])
101
114
  Einhorn::State.state = updated_state
102
115
  Einhorn::Event.restore_persistent_descriptors(parsed[:persistent_descriptors])
@@ -110,37 +123,21 @@ module Einhorn
110
123
  updated_state = old_state.dup
111
124
 
112
125
  # Handle changes in state format updates from previous einhorn versions
113
- if store == Einhorn::State
114
- # TODO: Drop this backwards compatibility hack when we hit 0.7
115
- if updated_state.include?(:reloading_for_preload_upgrade) &&
116
- !updated_state.include?(:reloading_for_upgrade)
117
- updated_state[:reloading_for_upgrade] = updated_state.delete(:reloading_for_preload_upgrade)
118
- message << "upgraded :reloading_for_preload_upgrade to :reloading_for_upgrade"
126
+ if store == Einhorn::State && updated_state[:children]
127
+ # Depending on what is passed for --reexec-as, it's possible
128
+ # that the process received a SIGCHLD while something other
129
+ # than einhorn was the active executable. If that happened,
130
+ # einhorn might not know about a dead child, so let's check
131
+ # them all
132
+ dead = []
133
+ updated_state[:children].each do |pid, v|
134
+ pid = Process.wait(pid, Process::WNOHANG)
135
+ dead << pid if pid
136
+ rescue Errno::ECHILD
137
+ dead << pid
119
138
  end
120
-
121
- if updated_state[:children]
122
- # For a period, we created children entries for state_passers,
123
- # but we don't want that (in particular, it probably died
124
- # before we could setup our SIGCHLD handler
125
- updated_state[:children].delete_if {|k, v| v[:type] == :state_passer}
126
-
127
- # Depending on what is passed for --reexec-as, it's possible
128
- # that the process received a SIGCHLD while something other
129
- # than einhorn was the active executable. If that happened,
130
- # einhorn might not know about a dead child, so let's check
131
- # them all
132
- dead = []
133
- updated_state[:children].each do |pid, v|
134
- begin
135
- pid = Process.wait(pid, Process::WNOHANG)
136
- dead << pid if pid
137
- rescue Errno::ECHILD
138
- dead << pid
139
- end
140
- end
141
- Einhorn::Event::Timer.open(0) do
142
- dead.each {|pid| Einhorn::Command.mourn(pid)}
143
- end
139
+ Einhorn::Event::Timer.open(0) do
140
+ dead.each { |pid| Einhorn::Command.cleanup(pid) }
144
141
  end
145
142
  end
146
143
 
@@ -149,12 +146,12 @@ module Einhorn
149
146
  deleted_keys = updated_state.keys - default.keys
150
147
  return [updated_state, message.first] if added_keys.length == 0 && deleted_keys.length == 0
151
148
 
152
- added_keys.each {|key| updated_state[key] = default[key]}
153
- deleted_keys.each {|key| updated_state.delete(key)}
149
+ added_keys.each { |key| updated_state[key] = default[key] }
150
+ deleted_keys.each { |key| updated_state.delete(key) }
154
151
 
155
152
  message << "adding default values for #{added_keys.inspect}"
156
153
  message << "deleting values for #{deleted_keys.inspect}"
157
- message = "State format for #{store_name} has changed: #{message.join(', ')}"
154
+ message = "State format for #{store_name} has changed: #{message.join(", ")}"
158
155
 
159
156
  # Can't print yet, since state hasn't been set, so we pass along the message.
160
157
  [updated_state, message]
@@ -169,42 +166,45 @@ module Einhorn
169
166
  sd = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
170
167
  Einhorn::Compat.cloexec!(sd, false)
171
168
 
172
- if flags.include?('r') || flags.include?('so_reuseaddr')
169
+ if flags.include?("r") || flags.include?("so_reuseaddr")
173
170
  sd.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
174
171
  end
175
172
 
176
173
  sd.bind(Socket.pack_sockaddr_in(port, addr))
177
174
  sd.listen(Einhorn::State.config[:backlog])
178
175
 
179
- if flags.include?('n') || flags.include?('o_nonblock')
176
+ if flags.include?("n") || flags.include?("o_nonblock")
180
177
  fl = sd.fcntl(Fcntl::F_GETFL)
181
178
  sd.fcntl(Fcntl::F_SETFL, fl | Fcntl::O_NONBLOCK)
182
179
  end
183
180
 
184
181
  Einhorn::TransientState.socket_handles << sd
185
- sd.fileno
182
+ [sd.fileno, sd.local_address.ip_port]
186
183
  end
187
184
 
188
185
  # Implement these ourselves so it plays nicely with state persistence
189
- def self.log_debug(msg, tag=nil)
190
- $stderr.puts("#{log_tag} DEBUG: #{msg}\n") if Einhorn::State.verbosity <= 0
191
- self.send_tagged_message(tag, msg) if tag
186
+ def self.log_debug(msg, tag = nil)
187
+ warn("#{log_tag} DEBUG: #{msg}\n") if Einhorn::State.verbosity <= 0
188
+ $stderr.flush
189
+ send_tagged_message(tag, msg) if tag
192
190
  end
193
- def self.log_info(msg, tag=nil)
194
- $stderr.puts("#{log_tag} INFO: #{msg}\n") if Einhorn::State.verbosity <= 1
195
- self.send_tagged_message(tag, msg) if tag
191
+
192
+ def self.log_info(msg, tag = nil)
193
+ warn("#{log_tag} INFO: #{msg}\n") if Einhorn::State.verbosity <= 1
194
+ $stderr.flush
195
+ send_tagged_message(tag, msg) if tag
196
196
  end
197
- def self.log_error(msg, tag=nil)
198
- $stderr.puts("#{log_tag} ERROR: #{msg}\n") if Einhorn::State.verbosity <= 2
199
- self.send_tagged_message(tag, "ERROR: #{msg}") if tag
197
+
198
+ def self.log_error(msg, tag = nil)
199
+ warn("#{log_tag} ERROR: #{msg}\n") if Einhorn::State.verbosity <= 2
200
+ $stderr.flush
201
+ send_tagged_message(tag, "ERROR: #{msg}") if tag
200
202
  end
201
203
 
202
- def self.send_tagged_message(tag, message, last=false)
204
+ def self.send_tagged_message(tag, message, last = false)
203
205
  Einhorn::Command::Interface.send_tagged_message(tag, message, last)
204
206
  end
205
207
 
206
- private
207
-
208
208
  def self.log_tag
209
209
  case whatami = Einhorn::TransientState.whatami
210
210
  when :master
@@ -217,17 +217,16 @@ module Einhorn
217
217
  "[UNKNOWN (#{whatami.inspect}) #{$$}]"
218
218
  end
219
219
  end
220
-
221
- public
220
+ private_class_method :log_tag
222
221
 
223
222
  def self.which(cmd)
224
- if cmd.include?('/')
225
- return cmd if File.exists?(cmd)
223
+ if cmd.include?("/")
224
+ return cmd if File.exist?(cmd)
226
225
  raise "Could not find #{cmd}"
227
226
  else
228
- ENV['PATH'].split(':').each do |f|
227
+ ENV["PATH"].split(":").each do |f|
229
228
  abs = File.join(f, cmd)
230
- return abs if File.exists?(abs)
229
+ return abs if File.exist?(abs)
231
230
  end
232
231
  raise "Could not find #{cmd} in PATH"
233
232
  end
@@ -237,29 +236,33 @@ module Einhorn
237
236
  def self.is_script(file)
238
237
  File.open(file) do |f|
239
238
  bytes = f.read(2)
240
- bytes == '#!'
239
+ bytes == "#!"
241
240
  end
242
241
  end
243
242
 
244
243
  def self.preload
245
- if path = Einhorn::State.path
244
+ if (path = Einhorn::State.path)
246
245
  set_argv(Einhorn::State.cmd, false)
247
246
 
248
247
  begin
248
+ # Reset preloaded state to false - this allows us to monitor for failed preloads during reloads.
249
+ Einhorn::State.preloaded = false
249
250
  # If it's not going to be requireable, then load it.
250
- if !path.end_with?('.rb') && File.exists?(path)
251
+ if !path.end_with?(".rb") && File.exist?(path)
251
252
  log_info("Loading #{path} (if this hangs, make sure your code can be properly loaded as a library)", :upgrade)
252
253
  load path
253
254
  else
254
255
  log_info("Requiring #{path} (if this hangs, make sure your code can be properly loaded as a library)", :upgrade)
255
256
  require path
257
+
258
+ force_move_to_oldgen if Einhorn::State.config[:gc_before_fork]
256
259
  end
257
- rescue Exception => e
260
+ rescue StandardError, LoadError => e
258
261
  log_info("Proceeding with postload -- could not load #{path}: #{e} (#{e.class})\n #{e.backtrace.join("\n ")}", :upgrade)
259
262
  else
260
263
  if defined?(einhorn_main)
261
264
  log_info("Successfully loaded #{path}", :upgrade)
262
- Einhorn::TransientState.preloaded = true
265
+ Einhorn::State.preloaded = true
263
266
  else
264
267
  log_info("Proceeding with postload -- loaded #{path}, but no einhorn_main method was defined", :upgrade)
265
268
  end
@@ -267,10 +270,26 @@ module Einhorn
267
270
  end
268
271
  end
269
272
 
273
+ # Make the GC more copy-on-write friendly by forcibly incrementing the generation
274
+ # counter on all objects to its maximum value. Learn more at: https://github.com/ko1/nakayoshi_fork
275
+ def self.force_move_to_oldgen
276
+ log_info("Starting GC to improve copy-on-write memory sharing", :upgrade)
277
+
278
+ GC.start
279
+ 3.times do
280
+ GC.start(full_mark: false)
281
+ end
282
+
283
+ GC.compact if GC.respond_to?(:compact)
284
+
285
+ log_info("Finished GC after preloading", :upgrade)
286
+ end
287
+ private_class_method :force_move_to_oldgen
288
+
270
289
  def self.set_argv(cmd, set_ps_name)
271
290
  # TODO: clean up this hack
272
291
  idx = 0
273
- if cmd[0] =~ /(^|\/)ruby$/
292
+ if /(^|\/)ruby$/.match?(cmd[0])
274
293
  idx = 1
275
294
  elsif !is_script(cmd[0])
276
295
  log_info("WARNING: Going to set $0 to #{cmd[idx]}, but it doesn't look like a script")
@@ -287,7 +306,7 @@ module Einhorn
287
306
  $0 = worker_ps_name
288
307
  end
289
308
 
290
- ARGV[0..-1] = cmd[idx+1..-1]
309
+ ARGV[0..-1] = cmd[idx + 1..-1]
291
310
  log_info("Set#{set_ps_name ? " $0 = #{$0.inspect}, " : nil} ARGV = #{ARGV.inspect}")
292
311
  end
293
312
 
@@ -300,15 +319,15 @@ module Einhorn
300
319
  end
301
320
 
302
321
  def self.worker_ps_name
303
- Einhorn::State.cmd_name ? "ruby #{Einhorn::State.cmd_name}" : Einhorn::State.orig_cmd.join(' ')
322
+ Einhorn::State.cmd_name ? "ruby #{Einhorn::State.cmd_name}" : Einhorn::State.orig_cmd.join(" ")
304
323
  end
305
324
 
306
325
  def self.renice_self
307
326
  whatami = Einhorn::TransientState.whatami
308
- return unless nice = Einhorn::State.nice[whatami]
327
+ return unless (nice = Einhorn::State.nice[whatami])
309
328
  pid = $$
310
329
 
311
- unless nice.kind_of?(Fixnum)
330
+ unless nice.is_a?(Integer)
312
331
  raise "Nice must be a fixnum: #{nice.inspect}"
313
332
  end
314
333
 
@@ -324,32 +343,15 @@ module Einhorn
324
343
 
325
344
  def self.socketify_env!
326
345
  Einhorn::State.bind.each do |host, port, flags|
327
- fd = bind(host, port, flags)
346
+ fd, actual_port = bind(host, port, flags)
328
347
  Einhorn::State.bind_fds << fd
329
- end
330
- end
331
-
332
- # This duplicates some code from the environment path, but is
333
- # deprecated so that's ok.
334
- def self.socketify!(cmd)
335
- cmd.map! do |arg|
336
- if arg =~ /^(.*=|)srv:([^:]+):(\d+)((?:,\w+)*)$/
337
- log_error("Using deprecated command-line configuration for Einhorn; should upgrade to the environment variable interface.")
338
- opt = $1
339
- host = $2
340
- port = $3
341
- flags = $4.split(',').select {|flag| flag.length > 0}.map {|flag| flag.downcase}
342
- fd = (Einhorn::State.sockets[[host, port]] ||= bind(host, port, flags))
343
- "#{opt}#{fd}"
344
- else
345
- arg
346
- end
348
+ Einhorn::State.bound_ports << actual_port
347
349
  end
348
350
  end
349
351
 
350
352
  # Construct and a command and args that can be used to re-exec
351
353
  # Einhorn for upgrades.
352
- def self.upgrade_commandline(einhorn_flags=[])
354
+ def self.upgrade_commandline(einhorn_flags = [])
353
355
  cmdline = []
354
356
  if Einhorn::State.reexec_commandline
355
357
  cmdline += Einhorn::State.reexec_commandline
@@ -357,7 +359,7 @@ module Einhorn
357
359
  cmdline << Einhorn::TransientState.script_name
358
360
  end
359
361
  cmdline += einhorn_flags
360
- cmdline << '--'
362
+ cmdline << "--"
361
363
  cmdline += Einhorn::State.cmd
362
364
  [cmdline[0], cmdline[1..-1]]
363
365
  end
@@ -369,7 +371,7 @@ module Einhorn
369
371
  upgrade_sentinel = fork do
370
372
  Einhorn::TransientState.whatami = :upgrade_sentinel
371
373
  Einhorn.initialize_reload_environment
372
- Einhorn::Compat.exec(*Einhorn.upgrade_commandline(['--upgrade-check']))
374
+ Einhorn::Compat.exec(*Einhorn.upgrade_commandline(["--upgrade-check"]))
373
375
  end
374
376
  Process.wait(upgrade_sentinel)
375
377
  $?.exitstatus.zero?
@@ -394,10 +396,10 @@ module Einhorn
394
396
  # startup. Currently, this means the bundler and rbenv versions.
395
397
  def self.dump_environment_info
396
398
  log_info("Running under Ruby #{RUBY_VERSION}", :environment)
397
- log_info("Rbenv ruby version: #{ENV['RBENV_VERSION']}", :environment) if ENV['RBENV_VERSION']
399
+ log_info("Rbenv ruby version: #{ENV["RBENV_VERSION"]}", :environment) if ENV["RBENV_VERSION"]
398
400
  begin
399
- bundler_gem = Gem::Specification.find_by_name('bundler')
400
- log_info("Using Bundler #{bundler_gem.version.to_s}", :environment)
401
+ bundler_gem = Gem::Specification.find_by_name("bundler")
402
+ log_info("Using Bundler #{bundler_gem.version}", :environment)
401
403
  rescue Gem::LoadError
402
404
  end
403
405
  end
@@ -418,7 +420,6 @@ module Einhorn
418
420
  # TODO: don't actually alter ARGV[0]?
419
421
  Einhorn::State.cmd[0] = which(Einhorn::State.cmd[0])
420
422
  socketify_env!
421
- socketify!(Einhorn::State.cmd)
422
423
  end
423
424
 
424
425
  set_master_ps_name
@@ -431,6 +432,14 @@ module Einhorn
431
432
  Einhorn::State.reloading_for_upgrade = false
432
433
  end
433
434
 
435
+ # If setting a signal-timeout, timeout the event loop
436
+ # in the same timeframe, ensuring processes are culled
437
+ # on a regular basis.
438
+ if Einhorn::State.signal_timeout
439
+ Einhorn::Event.default_timeout = Einhorn::Event.default_timeout.nil? ?
440
+ Einhorn::State.signal_timeout : [Einhorn::State.signal_timeout, Einhorn::Event.default_timeout].min
441
+ end
442
+
434
443
  while Einhorn::State.respawn || Einhorn::State.children.size > 0
435
444
  log_debug("Entering event loop")
436
445
 
@@ -445,10 +454,10 @@ module Einhorn
445
454
  end
446
455
  end
447
456
 
448
- require 'einhorn/command'
449
- require 'einhorn/compat'
450
- require 'einhorn/client'
451
- require 'einhorn/event'
452
- require 'einhorn/worker'
453
- require 'einhorn/worker_pool'
454
- require 'einhorn/version'
457
+ require "einhorn/command"
458
+ require "einhorn/compat"
459
+ require "einhorn/client"
460
+ require "einhorn/event"
461
+ require "einhorn/worker"
462
+ require "einhorn/worker_pool"
463
+ require "einhorn/version"