resqued 0.8.5 → 0.10.2

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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +23 -0
  3. data/exe/resqued +41 -22
  4. data/lib/resqued.rb +5 -5
  5. data/lib/resqued/config.rb +7 -7
  6. data/lib/resqued/config/after_fork.rb +1 -1
  7. data/lib/resqued/config/base.rb +1 -1
  8. data/lib/resqued/config/before_fork.rb +1 -1
  9. data/lib/resqued/config/worker.rb +13 -13
  10. data/lib/resqued/daemon.rb +1 -0
  11. data/lib/resqued/exec_on_hup.rb +43 -0
  12. data/lib/resqued/listener.rb +51 -49
  13. data/lib/resqued/listener_pool.rb +97 -0
  14. data/lib/resqued/listener_proxy.rb +40 -31
  15. data/lib/resqued/listener_state.rb +8 -0
  16. data/lib/resqued/logging.rb +15 -8
  17. data/lib/resqued/master.rb +94 -98
  18. data/lib/resqued/master_state.rb +73 -0
  19. data/lib/resqued/procline_version.rb +2 -2
  20. data/lib/resqued/sleepy.rb +6 -4
  21. data/lib/resqued/test_case.rb +3 -3
  22. data/lib/resqued/version.rb +1 -1
  23. data/lib/resqued/worker.rb +18 -13
  24. data/spec/fixtures/test_case_after_fork_raises.rb +5 -2
  25. data/spec/fixtures/test_case_before_fork_raises.rb +4 -1
  26. data/spec/fixtures/test_case_environment.rb +3 -1
  27. data/spec/integration/master_inherits_child_spec.rb +85 -0
  28. data/spec/integration/restart_spec.rb +63 -0
  29. data/spec/resqued/backoff_spec.rb +27 -27
  30. data/spec/resqued/config/fork_event_spec.rb +8 -8
  31. data/spec/resqued/config/worker_spec.rb +63 -50
  32. data/spec/resqued/config_spec.rb +6 -6
  33. data/spec/resqued/sleepy_spec.rb +10 -11
  34. data/spec/resqued/test_case_spec.rb +7 -7
  35. data/spec/spec_helper.rb +5 -1
  36. data/spec/support/custom_matchers.rb +10 -2
  37. data/spec/support/extra-child-shim +6 -0
  38. data/spec/support/resqued_path.rb +11 -0
  39. metadata +44 -27
  40. data/exe/resqued-listener +0 -6
@@ -0,0 +1,97 @@
1
+ require "resqued/listener_proxy"
2
+ require "resqued/listener_state"
3
+
4
+ module Resqued
5
+ class ListenerPool
6
+ include Enumerable
7
+
8
+ # Public: Initialize a new pool, and store state in the given master's state.
9
+ def initialize(master_state)
10
+ @master_state = master_state
11
+ @listener_proxies = {}
12
+ # If this master is replacing an old one, there will be listeners in the state already.
13
+ @master_state.listener_states.each do |pid, ls|
14
+ @listener_proxies[pid] = ListenerProxy.new(ls)
15
+ end
16
+ end
17
+
18
+ # Public: Iterate through all active ListenerProxy instances.
19
+ def each(&block)
20
+ @listener_proxies.values.each(&block)
21
+ end
22
+
23
+ # Public: Number of active listeners.
24
+ def size
25
+ @listener_proxies.size
26
+ end
27
+
28
+ # Public: Are the listeners all gone?
29
+ def empty?
30
+ @listener_proxies.empty?
31
+ end
32
+
33
+ # Public: Initialize a new listener, run it, and record it as the current listener. Returns its ListenerProxy.
34
+ def start!
35
+ listener_state = ListenerState.new
36
+ listener_state.options = {
37
+ config_paths: @master_state.config_paths,
38
+ old_workers: map { |l| l.running_workers }.flatten,
39
+ listener_id: next_listener_id,
40
+ }
41
+ listener = ListenerProxy.new(listener_state)
42
+ listener.run
43
+ @master_state.listener_states[listener.pid] = listener_state
44
+ @listener_proxies[listener.pid] = listener
45
+ @master_state.current_listener_pid = listener.pid
46
+ return listener
47
+ end
48
+
49
+ # Public: Remove the given pid from the set of known listeners, and return its ListenerProxy.
50
+ def delete(pid)
51
+ @master_state.listener_states.delete(pid)
52
+ return @listener_proxies.delete(pid)
53
+ end
54
+
55
+ # Public: The current ListenerProxy, if available.
56
+ def current
57
+ @listener_proxies[current_pid]
58
+ end
59
+
60
+ # Public: The pid of the current listener, if available.
61
+ def current_pid
62
+ @master_state.current_listener_pid
63
+ end
64
+
65
+ # Public: Don't consider the current listener to be current anymore.
66
+ def clear_current!
67
+ @master_state.current_listener_pid = nil
68
+ end
69
+
70
+ # Public: Change the current listener into the last good listener.
71
+ def cycle_current
72
+ @master_state.last_good_listener_pid = @master_state.current_listener_pid
73
+ @master_state.current_listener_pid = nil
74
+ end
75
+
76
+ # Public: The last good (previous current) ListenerProxy, if available.
77
+ def last_good
78
+ @listener_proxies[last_good_pid]
79
+ end
80
+
81
+ # Public: The pid of the last good listener, if available.
82
+ def last_good_pid
83
+ @master_state.last_good_listener_pid
84
+ end
85
+
86
+ # Public: Forget which listener was the last good one.
87
+ def clear_last_good!
88
+ @master_state.last_good_listener_pid = nil
89
+ end
90
+
91
+ private
92
+
93
+ def next_listener_id
94
+ @master_state.listeners_created += 1
95
+ end
96
+ end
97
+ end
@@ -1,8 +1,8 @@
1
- require 'fcntl'
2
- require 'socket'
1
+ require "fcntl"
2
+ require "socket"
3
3
 
4
- require 'resqued/listener'
5
- require 'resqued/logging'
4
+ require "resqued/listener"
5
+ require "resqued/logging"
6
6
 
7
7
  module Resqued
8
8
  # Controls a listener process from the master process.
@@ -10,41 +10,46 @@ module Resqued
10
10
  include Resqued::Logging
11
11
 
12
12
  # Public.
13
- def initialize(options)
14
- @options = options
13
+ def initialize(state)
14
+ @state = state
15
15
  end
16
16
 
17
+ attr_reader :state
18
+
17
19
  # Public: wrap up all the things, this object is going home.
18
20
  def dispose
19
- if @master_socket
20
- @master_socket.close
21
- @master_socket = nil
21
+ if @state.master_socket
22
+ @state.master_socket.close
23
+ @state.master_socket = nil
22
24
  end
23
25
  end
24
26
 
25
27
  # Public: An IO to select on to check if there is incoming data available.
26
28
  def read_pipe
27
- @master_socket
29
+ @state.master_socket
28
30
  end
29
31
 
30
32
  # Public: The pid of the running listener process.
31
- attr_reader :pid
33
+ def pid
34
+ @state.pid
35
+ end
32
36
 
33
37
  # Public: Start the listener process.
34
38
  def run
35
39
  return if pid
40
+
36
41
  listener_socket, master_socket = UNIXSocket.pair
37
- if @pid = fork
42
+ if @state.pid = fork
38
43
  # master
39
44
  listener_socket.close
40
45
  master_socket.close_on_exec = true
41
- log "Started listener #{@pid}"
42
- @master_socket = master_socket
46
+ log "Started listener #{@state.pid}"
47
+ @state.master_socket = master_socket
43
48
  else
44
49
  # listener
45
50
  master_socket.close
46
- Master::TRAPS.each { |signal| trap(signal, 'DEFAULT') rescue nil }
47
- Listener.new(@options.merge(:socket => listener_socket)).exec
51
+ Master::TRAPS.each { |signal| trap(signal, "DEFAULT") rescue nil }
52
+ Listener.new(@state.options.merge(socket: listener_socket)).exec
48
53
  exit
49
54
  end
50
55
  end
@@ -57,46 +62,50 @@ module Resqued
57
62
 
58
63
  # Public: Get the list of workers running from this listener.
59
64
  def running_workers
60
- worker_pids.map { |pid, queue| { :pid => pid, :queue => queue } }
65
+ worker_pids.map { |pid, queue_key| { pid: pid, queue_key: queue_key } }
61
66
  end
62
67
 
63
68
  # Private: Map worker pids to queue names
64
69
  def worker_pids
65
- @worker_pids ||= {}
70
+ @state.worker_pids ||= {}
66
71
  end
67
72
 
68
73
  # Public: Check for updates on running worker information.
69
74
  def read_worker_status(options)
70
75
  on_activity = options[:on_activity]
71
- until @master_socket.nil?
72
- IO.select([@master_socket], nil, nil, 0) or return
73
- case line = @master_socket.readline
76
+ until @state.master_socket.nil?
77
+ IO.select([@state.master_socket], nil, nil, 0) or return
78
+ case line = @state.master_socket.readline
74
79
  when /^\+(\d+),(.*)$/
75
80
  worker_pids[$1] = $2
76
- on_activity.worker_started($1) if on_activity
81
+ on_activity&.worker_started($1)
77
82
  when /^-(\d+)$/
78
83
  worker_pids.delete($1)
79
- on_activity.worker_finished($1) if on_activity
84
+ on_activity&.worker_finished($1)
80
85
  when /^RUNNING/
81
- on_activity.listener_running(self) if on_activity
82
- when ''
86
+ on_activity&.listener_running(self)
87
+ when ""
83
88
  break
84
89
  else
85
90
  log "Malformed data from listener: #{line.inspect}"
86
91
  end
87
92
  end
88
93
  rescue EOFError, Errno::ECONNRESET
89
- @master_socket.close
90
- @master_socket = nil
94
+ @state.master_socket.close
95
+ @state.master_socket = nil
91
96
  end
92
97
 
93
98
  # Public: Tell the listener process that a worker finished.
94
99
  def worker_finished(pid)
95
- return if @master_socket.nil?
96
- @master_socket.puts(pid)
100
+ return if @state.master_socket.nil?
101
+
102
+ @state.master_socket.write_nonblock("#{pid}\n")
103
+ rescue IO::WaitWritable
104
+ log "Couldn't tell #{@state.pid} that #{pid} exited!"
105
+ # Ignore it, maybe the next time it'll work.
97
106
  rescue Errno::EPIPE
98
- @master_socket.close
99
- @master_socket = nil
107
+ @state.master_socket.close
108
+ @state.master_socket = nil
100
109
  end
101
110
  end
102
111
  end
@@ -0,0 +1,8 @@
1
+ module Resqued
2
+ class ListenerState
3
+ attr_accessor :master_socket
4
+ attr_accessor :options
5
+ attr_accessor :pid
6
+ attr_accessor :worker_pids
7
+ end
8
+ end
@@ -1,4 +1,4 @@
1
- require 'mono_logger'
1
+ require "mono_logger"
2
2
 
3
3
  module Resqued
4
4
  # Mixin for any class that wants to write messages to the log file.
@@ -18,27 +18,34 @@ module Resqued
18
18
 
19
19
  class ResquedLogFormatter < ::Logger::Formatter
20
20
  def call(severity, time, progname, msg)
21
- "[%s#%6d] %5s %s -- %s\n" % [format_datetime(time), $$, severity, progname, msg2str(msg)]
21
+ sprintf "[%s#%6d] %5s %s -- %s\n",
22
+ format_datetime(time),
23
+ $$,
24
+ severity,
25
+ progname,
26
+ msg2str(msg)
22
27
  end
23
28
  end
24
29
 
25
30
  # Private: Lets our logger reopen its logfile without monologger EVEN KNOWING.
26
31
  class ResquedLoggingIOWrapper
32
+ # rubocop: disable Style/MethodMissingSuper
27
33
  def method_missing(*args)
28
34
  ::Resqued::Logging.logging_io.send(*args)
29
35
  end
36
+ # rubocop: enable Style/MethodMissingSuper
30
37
 
31
- def respond_to?(*args)
32
- ::Resqued::Logging.logging_io.respond_to?(*args)
38
+ def respond_to_missing?(method, *)
39
+ ::Resqued::Logging.logging_io.respond_to?(method)
33
40
  end
34
41
  end
35
42
 
36
43
  # Private: Get an IO to write log messages to.
37
44
  def logging_io
38
- @logging_io = nil if @logging_io && @logging_io.closed?
45
+ @logging_io = nil if @logging_io&.closed?
39
46
  @logging_io ||=
40
47
  if path = Resqued::Logging.log_file
41
- File.open(path, 'a').tap do |f|
48
+ File.open(path, "a").tap do |f|
42
49
  f.sync = true
43
50
  f.close_on_exec = true
44
51
  # Make sure we're not holding onto a stale filehandle.
@@ -60,13 +67,13 @@ module Resqued
60
67
 
61
68
  # Public.
62
69
  def log_file=(path)
63
- ENV['RESQUED_LOGFILE'] = File.expand_path(path)
70
+ ENV["RESQUED_LOGFILE"] = File.expand_path(path)
64
71
  close_log
65
72
  end
66
73
 
67
74
  # Public.
68
75
  def log_file
69
- ENV['RESQUED_LOGFILE']
76
+ ENV["RESQUED_LOGFILE"]
70
77
  end
71
78
  end
72
79
 
@@ -1,9 +1,11 @@
1
- require 'resqued/backoff'
2
- require 'resqued/listener_proxy'
3
- require 'resqued/logging'
4
- require 'resqued/pidfile'
5
- require 'resqued/procline_version'
6
- require 'resqued/sleepy'
1
+ require "resqued/backoff"
2
+ require "resqued/exec_on_hup"
3
+ require "resqued/listener_pool"
4
+ require "resqued/logging"
5
+ require "resqued/master_state"
6
+ require "resqued/pidfile"
7
+ require "resqued/procline_version"
8
+ require "resqued/sleepy"
7
9
 
8
10
  module Resqued
9
11
  # The master process.
@@ -16,19 +18,17 @@ module Resqued
16
18
  include Resqued::ProclineVersion
17
19
  include Resqued::Sleepy
18
20
 
19
- def initialize(options)
20
- @config_paths = options.fetch(:config_paths)
21
- @pidfile = options.fetch(:master_pidfile) { nil }
22
- @status_pipe = options.fetch(:status_pipe) { nil }
23
- @fast_exit = options.fetch(:fast_exit) { false }
21
+ def initialize(state, options = {})
22
+ @state = state
23
+ @status_pipe = options.fetch(:status_pipe, nil)
24
+ @listeners = ListenerPool.new(state)
24
25
  @listener_backoff = Backoff.new
25
- @listeners_created = 0
26
26
  end
27
27
 
28
28
  # Public: Starts the master process.
29
29
  def run(ready_pipe = nil)
30
30
  report_unexpected_exits
31
- with_pidfile(@pidfile) do
31
+ with_pidfile(@state.pidfile) do
32
32
  write_procline
33
33
  install_signal_handlers
34
34
  if ready_pipe
@@ -42,32 +42,39 @@ module Resqued
42
42
 
43
43
  # Private: dat main loop.
44
44
  def go_ham
45
+ # If we're resuming, we'll want to recycle the existing listener now.
46
+ prepare_new_listener
47
+
45
48
  loop do
46
49
  read_listeners
47
50
  reap_all_listeners(Process::WNOHANG)
48
- start_listener unless @paused
51
+ start_listener unless @state.paused
49
52
  case signal = SIGNAL_QUEUE.shift
50
53
  when nil
51
54
  yawn(@listener_backoff.how_long? || 30.0)
52
55
  when :INFO
53
56
  dump_object_counts
54
57
  when :HUP
58
+ if @state.exec_on_hup
59
+ log "Execing a new master"
60
+ ExecOnHUP.exec!(@state)
61
+ end
55
62
  reopen_logs
56
63
  log "Restarting listener with new configuration and application."
57
64
  prepare_new_listener
58
65
  when :USR2
59
66
  log "Pause job processing"
60
- @paused = true
61
- kill_listener(:QUIT, @current_listener)
62
- @current_listener = nil
67
+ @state.paused = true
68
+ kill_listener(:QUIT, @listeners.current)
69
+ @listeners.clear_current!
63
70
  when :CONT
64
71
  log "Resume job processing"
65
- @paused = false
72
+ @state.paused = false
66
73
  kill_all_listeners(:CONT)
67
74
  when :INT, :TERM, :QUIT
68
75
  log "Shutting down..."
69
76
  kill_all_listeners(signal)
70
- wait_for_workers unless @fast_exit
77
+ wait_for_workers unless @state.fast_exit
71
78
  break
72
79
  end
73
80
  end
@@ -85,14 +92,14 @@ module Resqued
85
92
  end
86
93
  top = 10
87
94
  log "#{total} objects. top #{top}:"
88
- counts.sort_by { |name, count| count }.reverse.each_with_index do |(name, count), i|
89
- if i < top
90
- diff = ""
91
- if last = @last_counts && @last_counts[name]
92
- diff = " (#{'%+d' % (count - last)})"
93
- end
94
- log " #{count} #{name}#{diff}"
95
+ counts.sort_by { |_, count| -count }.each_with_index do |(name, count), i|
96
+ next unless i < top
97
+
98
+ diff = ""
99
+ if last = @last_counts && @last_counts[name]
100
+ diff = sprintf(" (%+d)", (count - last))
95
101
  end
102
+ log " #{count} #{name}#{diff}"
96
103
  end
97
104
  @last_counts = counts
98
105
  log GC.stat.inspect
@@ -100,59 +107,45 @@ module Resqued
100
107
  log "Error while counting objects: #{e}"
101
108
  end
102
109
 
103
- # Private: Map listener pids to ListenerProxy objects.
104
- def listener_pids
105
- @listener_pids ||= {}
106
- end
107
-
108
- # Private: All the ListenerProxy objects.
109
- def all_listeners
110
- listener_pids.values
111
- end
112
-
113
110
  def start_listener
114
- return if @current_listener || @listener_backoff.wait?
115
- @current_listener = ListenerProxy.new(:config_paths => @config_paths, :old_workers => all_listeners.map { |l| l.running_workers }.flatten, :listener_id => next_listener_id)
116
- @current_listener.run
117
- listener_status @current_listener, 'start'
111
+ return if @listeners.current || @listener_backoff.wait?
112
+
113
+ listener = @listeners.start!
114
+ listener_status listener, "start"
118
115
  @listener_backoff.started
119
- listener_pids[@current_listener.pid] = @current_listener
120
116
  write_procline
121
117
  end
122
118
 
123
- def next_listener_id
124
- @listeners_created += 1
125
- end
126
-
127
119
  def read_listeners
128
- all_listeners.each do |l|
129
- l.read_worker_status(:on_activity => self)
120
+ @listeners.each do |l|
121
+ l.read_worker_status(on_activity: self)
130
122
  end
131
123
  end
132
124
 
133
125
  # Listener message: A worker just started working.
134
126
  def worker_started(pid)
135
- worker_status(pid, 'start')
127
+ worker_status(pid, "start")
136
128
  end
137
129
 
138
130
  # Listener message: A worker just stopped working.
139
131
  #
140
132
  # Forwards the message to the other listeners.
141
133
  def worker_finished(pid)
142
- worker_status(pid, 'stop')
143
- all_listeners.each do |other|
134
+ worker_status(pid, "stop")
135
+ @listeners.each do |other|
144
136
  other.worker_finished(pid)
145
137
  end
146
138
  end
147
139
 
148
- # Listener message: A listener finished booting, and is ready to start workers.
140
+ # Listener message: A listener finished booting, and is ready to
141
+ # start workers.
149
142
  #
150
143
  # Promotes a booting listener to be the current listener.
151
144
  def listener_running(listener)
152
- listener_status(listener, 'ready')
153
- if listener == @current_listener
154
- kill_listener(:QUIT, @last_good_listener)
155
- @last_good_listener = nil
145
+ listener_status(listener, "ready")
146
+ if listener == @listeners.current
147
+ kill_listener(:QUIT, @listeners.last_good)
148
+ @listeners.clear_last_good!
156
149
  else
157
150
  # This listener didn't receive the last SIGQUIT we sent.
158
151
  # (It was probably sent before the listener had set up its traps.)
@@ -165,23 +158,26 @@ module Resqued
165
158
  #
166
159
  # The old one will be killed when the new one is ready for workers.
167
160
  def prepare_new_listener
168
- if @last_good_listener
169
- # The last_good_listener is still running because we got another HUP before the new listener finished booting.
170
- # Keep the last_good_listener (where all the workers are) and kill the booting current_listener. We'll start a new one.
171
- kill_listener(:QUIT, @current_listener)
161
+ if @listeners.last_good
162
+ # The last good listener is still running because we got another
163
+ # HUP before the new listener finished booting.
164
+ # Keep the last_good_listener (where all the workers are) and
165
+ # kill the booting current_listener. We'll start a new one.
166
+ kill_listener(:QUIT, @listeners.current)
167
+ # Indicate to `start_listener` that it should start a new
168
+ # listener.
169
+ @listeners.clear_current!
172
170
  else
173
- @last_good_listener = @current_listener
171
+ @listeners.cycle_current
174
172
  end
175
- # Indicate to `start_listener` that it should start a new listener.
176
- @current_listener = nil
177
173
  end
178
174
 
179
175
  def kill_listener(signal, listener)
180
- listener.kill(signal) if listener
176
+ listener&.kill(signal)
181
177
  end
182
178
 
183
179
  def kill_all_listeners(signal)
184
- all_listeners.each do |l|
180
+ @listeners.each do |l|
185
181
  l.kill(signal)
186
182
  end
187
183
  end
@@ -191,80 +187,80 @@ module Resqued
191
187
  end
192
188
 
193
189
  def reap_all_listeners(waitpid_flags = 0)
194
- begin
195
- lpid, status = Process.waitpid2(-1, waitpid_flags)
196
- if lpid
190
+ until @listeners.empty?
191
+ begin
192
+ lpid, status = Process.waitpid2(-1, waitpid_flags)
193
+ return unless lpid
194
+
197
195
  log "Listener exited #{status}"
198
- if @current_listener && @current_listener.pid == lpid
196
+
197
+ if @listeners.current_pid == lpid
199
198
  @listener_backoff.died
200
- @current_listener = nil
199
+ @listeners.clear_current!
200
+ end
201
+
202
+ if @listeners.last_good_pid == lpid
203
+ @state.clear_last_good!
201
204
  end
202
205
 
203
- if @last_good_listener && @last_good_listener.pid == lpid
204
- @last_good_listener = nil
206
+ if dead_listener = @listeners.delete(lpid)
207
+ listener_status dead_listener, "stop"
208
+ dead_listener.dispose
205
209
  end
206
- dead_listener = listener_pids.delete(lpid)
207
- listener_status dead_listener, 'stop'
208
- dead_listener.dispose
210
+
209
211
  write_procline
210
- else
212
+ rescue Errno::ECHILD
211
213
  return
212
214
  end
213
- rescue Errno::ECHILD
214
- return
215
- end while true
215
+ end
216
216
  end
217
217
 
218
- SIGNALS = [ :HUP, :INT, :USR2, :CONT, :TERM, :QUIT ]
219
- OPTIONAL_SIGNALS = [ :INFO ]
220
- OTHER_SIGNALS = [:CHLD, 'EXIT']
218
+ SIGNALS = [:HUP, :INT, :USR2, :CONT, :TERM, :QUIT].freeze
219
+ OPTIONAL_SIGNALS = [:INFO].freeze
220
+ OTHER_SIGNALS = [:CHLD, "EXIT"].freeze
221
221
  TRAPS = SIGNALS + OPTIONAL_SIGNALS + OTHER_SIGNALS
222
222
 
223
- SIGNAL_QUEUE = []
223
+ SIGNAL_QUEUE = [] # rubocop: disable Style/MutableConstant
224
224
 
225
225
  def install_signal_handlers
226
226
  trap(:CHLD) { awake }
227
- SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal ; awake } }
228
- OPTIONAL_SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal ; awake } rescue nil }
227
+ SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal; awake } }
228
+ OPTIONAL_SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal; awake } rescue nil }
229
229
  end
230
230
 
231
231
  def report_unexpected_exits
232
- trap('EXIT') do
232
+ trap("EXIT") do
233
233
  log("EXIT #{$!.inspect}")
234
- if $!
235
- $!.backtrace.each do |line|
236
- log(line)
237
- end
234
+ $!&.backtrace&.each do |line|
235
+ log(line)
238
236
  end
239
237
  end
240
238
  end
241
239
 
242
240
  def no_more_unexpected_exits
243
- trap('EXIT', 'DEFAULT')
241
+ trap("EXIT", "DEFAULT")
244
242
  end
245
243
 
246
244
  def yawn(duration)
247
- super(duration, all_listeners.map { |l| l.read_pipe })
245
+ super(duration, @listeners.map { |l| l.read_pipe })
248
246
  end
249
247
 
250
248
  def write_procline
251
- $0 = "#{procline_version} master [gen #{@listeners_created}] [#{listener_pids.size} running] #{ARGV.join(' ')}"
249
+ $0 = "#{procline_version} master [gen #{@state.listeners_created}] [#{@listeners.size} running] #{ARGV.join(' ')}"
252
250
  end
253
251
 
254
252
  def listener_status(listener, status)
255
- if listener && listener.pid
256
- status_message('listener', listener.pid, status)
253
+ if listener&.pid
254
+ status_message("listener", listener.pid, status)
257
255
  end
258
256
  end
259
257
 
260
258
  def worker_status(pid, status)
261
- status_message('worker', pid, status)
259
+ status_message("worker", pid, status)
262
260
  end
263
261
 
264
262
  def status_message(type, pid, status)
265
- if @status_pipe
266
- @status_pipe.write("#{type},#{pid},#{status}\n")
267
- end
263
+ @status_pipe&.write("#{type},#{pid},#{status}\n")
268
264
  end
269
265
  end
270
266
  end