main_loop 0.1.4.364822 → 0.1.4.369901

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.
@@ -1,13 +1,50 @@
1
1
  require 'monitor'
2
2
  require 'logger'
3
3
 
4
+ # = MainLoop::Dispatcher
5
+ #
6
+ # Координирует обработчиков, управляет жизненным циклом, обрабатывает сигнал терминации с таймаутом.
7
+ #
8
+ # Использует {MonitorMixin} для потоко-безопасности. Управляет списком обработчиков ({handlers})
9
+ # и обеспечивает корректное завершение всех обработчиков при получении сигнала терминации.
10
+ #
11
+ # == Жизненный цикл обработчиков
12
+ #
13
+ # 1. Регистрация через {add_handler}
14
+ # 2. Обработка сигнала терминации через {term}
15
+ # 3. Если не завершились за timeout → {kill}
16
+ # 4. {try_exit!} когда все завершены
17
+ #
18
+ # == Пример использования
19
+ #
20
+ # bus = MainLoop::Bus.new
21
+ # dispatcher = MainLoop::Dispatcher.new(bus, timeout: 10, logger: logger)
22
+ #
23
+ # MainLoop::ProcessHandler.new dispatcher, 'worker' do
24
+ # # код процесса
25
+ # end
26
+ #
27
+ # dispatcher.term # инициировать терминацию
28
+ #
29
+ # == См. также
30
+ # - {MainLoop::Bus} — канал событий
31
+ # - {MainLoop::Handler} — базовый класс обработчиков
32
+ # - {MainLoop::Loop} — координирует Dispatcher
33
+
4
34
  module MainLoop
5
35
  class Dispatcher
6
-
7
36
  include MonitorMixin
8
37
 
9
38
  attr_reader :bus, :handlers, :logger
10
39
 
40
+ # == Инициализация
41
+ #
42
+ # @param bus [Bus] канал событий
43
+ # @param timeout [Integer] таймаут для принудительного завершения в секундах (по умолчанию 5)
44
+ # @param logger [Logger] логгер (по умолчанию Logger.new(nil))
45
+ # @option bus [Bus]
46
+ # @option timeout [Integer] 5
47
+ # @option logger [Logger] nil
11
48
  def initialize(bus, timeout: 5, logger: nil)
12
49
  super()
13
50
  @bus = bus
@@ -17,12 +54,25 @@ module MainLoop
17
54
  @exit_code = 0
18
55
  end
19
56
 
57
+ # == Обработка завершения процессов
58
+ #
59
+ # Обрабатывает массив завершенных процессов.
60
+ #
61
+ # @param statuses [Array<Array>] массив пар (pid, status)
62
+ # @example
63
+ # dispatcher.reap([[123, status], [456, nil]])
20
64
  def reap(statuses)
21
65
  statuses.each do |(pid, status)|
22
66
  reap_by_id(pid, status)
23
67
  end
24
68
  end
25
69
 
70
+ # == Обработка завершения процесса по ID
71
+ #
72
+ # Находит обработчика по ID и вызывает его {Handler#reap}.
73
+ #
74
+ # @param id [String] идентификатор обработчика (pid для процессов, object_id для потоков)
75
+ # @param status [Process::Status|nil] статус завершения или nil если неизвестен
26
76
  def reap_by_id(id, status)
27
77
  synchronize do
28
78
  if (handler = handlers.find {|h| h.id == id })
@@ -34,6 +84,12 @@ module MainLoop
34
84
  end
35
85
  end
36
86
 
87
+ # == Регистрация обработчика
88
+ #
89
+ # Добавляет обработчик в список. Если уже происходит терминация,
90
+ # сразу посылает `term` новому обработчику.
91
+ #
92
+ # @param handler [Handler] обработчик для регистрации
37
93
  def add_handler(handler)
38
94
  synchronize do
39
95
  handler.term if terminating?
@@ -41,10 +97,24 @@ module MainLoop
41
97
  end
42
98
  end
43
99
 
100
+ # == Проверка терминации
101
+ #
102
+ # @return [Time|nil] момент начала терминации или nil если не терминация
44
103
  def terminating?
45
104
  @terminating_at
46
105
  end
47
106
 
107
+ # == Инициировать терминацию
108
+ #
109
+ # Отправляет сигнал терминации всем обработчикам.
110
+ # Если уже в процессе терминации — принудительное завершение (kill).
111
+ #
112
+ # Если это первый вызов:
113
+ # - Устанавливает @terminating_at = Time.now
114
+ # - Отправляет term каждому обработчику
115
+ #
116
+ # Если уже терминация:
117
+ # - Отправляет kill каждому обработчику
48
118
  def term
49
119
  synchronize do
50
120
  if terminating?
@@ -58,11 +128,19 @@ module MainLoop
58
128
  end
59
129
  end
60
130
 
131
+ # == Отправить сигнал аварийного завершения
132
+ #
133
+ # Устанавливает код выхода 3 и инициирует терминацию.
134
+ # Если уже терминация — ничего не делает.
61
135
  def crash
62
136
  @exit_code = 3
63
137
  term unless terminating?
64
138
  end
65
139
 
140
+ # == Тик цикла диспетчера
141
+ #
142
+ # Вызывается в каждом цикле {MainLoop#start_loop_forever}.
143
+ # Проверяет необходимость принудительного завершения по timeout.
66
144
  def tick
67
145
  log_status if logger.debug?
68
146
  return unless terminating?
@@ -76,14 +154,30 @@ module MainLoop
76
154
  handlers.each(&:kill)
77
155
  end
78
156
 
157
+ # == Проверка необходимости принудительного завершения
158
+ #
159
+ # Проверяет, превышен ли timeout с момента начала терминации.
160
+ #
161
+ # @return [Boolean] true если timeout превышен
79
162
  def need_force_kill?
80
163
  @terminating_at && (Time.now - @terminating_at) >= @timeout
81
164
  end
82
165
 
166
+ # == Получить список PID процессов
167
+ #
168
+ # @return [Array<Integer>] массив PID всех процессовых обработчиков
83
169
  def pids
84
170
  handlers.map{|h| h.pid rescue nil }.compact
85
171
  end
86
172
 
173
+ # == Завершить программу
174
+ #
175
+ # Если все обработчики завершены, вызывает exit с соответствующим кодом.
176
+ #
177
+ # Код выхода:
178
+ # - @exit_code если все обработчики завершились успешно
179
+ # - 1 если любой обработчик завершился с ошибкой
180
+ #
87
181
  # :nocov:
88
182
  def try_exit!
89
183
  synchronize do
@@ -97,16 +191,19 @@ module MainLoop
97
191
  end
98
192
  # :nocov:
99
193
 
194
+ # == Логировать статус
195
+ #
196
+ # Логирует текущее состояние обработчиков (DEBUG уровень).
197
+ # Формат: "Total:N Running:M Finished:K. Success:[] TERM"
198
+ #
100
199
  # :nocov:
101
200
  def log_status
102
201
  total = handlers.size
103
202
  running = handlers.count(&:running?)
104
203
  finihsed = handlers.count(&:finished?)
105
204
  term_text = terminating? ? 'TERM' : ''
106
- logger.debug("Total:#{total} Running:#{running} Finihsed:#{finihsed}. #{term_text}".strip)
205
+ logger.debug("Total:#{total} Running:#{running} Finished:#{finihsed} Success:#{handlers.map {|h| h.success?}.to_s}. #{term_text}".strip)
107
206
  end
108
207
  # :nocov:
109
-
110
208
  end
111
209
  end
112
-
@@ -1,10 +1,37 @@
1
1
  require 'logger'
2
2
 
3
+ # = MainLoop::Handler
4
+ #
5
+ # Абстрактный базовый класс для обработчиков процессов и потоков.
6
+ #
7
+ # Определяет интерфейс и общую логику:
8
+ # - управление retry_count (количество повторов)
9
+ # - логика handle_retry
10
+ # - предикаты (finished?, success?, running?, terminating?)
11
+ # - обратный вызов on_term
12
+ #
13
+ # == Абстрактные методы (для реализации в подклассах)
14
+ #
15
+ # - {#id} — идентификатор обработчика (pid для процессов, object_id для потоков)
16
+ # - {#term} — отправка сигнала терминации
17
+ # - {#run} — запуск обработчика
18
+ # - {#kill} — принудительное завершение
19
+ # - {#reap(status)} — обработка завершения процесса/потока
20
+ #
21
+ # == См. также
22
+ # - {MainLoop::ProcessHandler} — реализация для процессов
23
+ # - {MainLoop::ThreadHandler} — реализация для потоков
24
+
3
25
  module MainLoop
4
26
  class Handler
5
-
6
27
  attr_reader :dispatcher, :name, :logger
7
28
 
29
+ # == Инициализация
30
+ #
31
+ # @param dispatcher [Dispatcher] ссылка на диспетчер
32
+ # @param name [String] имя обработчика
33
+ # @param retry_count [Integer, :unlimited] количество повторов после завершения
34
+ # @param logger [Logger] логгер (по умолчанию Logger.new(nil))
8
35
  def initialize(dispatcher, name, *_args, retry_count: 0, logger: nil, **_kwargs)
9
36
  @dispatcher = dispatcher
10
37
  @name = name
@@ -14,70 +41,116 @@ module MainLoop
14
41
  @handler_type = 'Unknown'
15
42
  end
16
43
 
44
+ # == Идентификатор (абстрактный)
45
+ #
46
+ # @return [String] идентификатор обработчика
47
+ # @raise [RuntimeError] если не реализован в подклассе
17
48
  # :nocov:
18
49
  def id(*_args)
19
50
  raise 'not implemented!'
20
51
  end
21
52
  # :nocov:
22
53
 
54
+ # == Терминация (абстрактный)
55
+ #
56
+ # Отправляет сигнал терминации обработчику.
57
+ # @raise [RuntimeError] если не реализован в подклассе
23
58
  # :nocov:
24
59
  def term(*_args)
25
60
  raise 'not implemented!'
26
61
  end
27
62
  # :nocov:
28
63
 
64
+ # == Запуск (абстрактный)
65
+ #
66
+ # Запускает обработчик.
67
+ # @raise [RuntimeError] если не реализован в подклассе
29
68
  # :nocov:
30
69
  def run(*_args)
31
70
  raise 'not implemented!'
32
71
  end
33
72
  # :nocov:
34
73
 
74
+ # == Принудительное завершение (абстрактный)
75
+ #
76
+ # Принудительно завершает обработчик.
77
+ # @raise [RuntimeError] если не реализован в подклассе
35
78
  # :nocov:
36
79
  def kill(*_args)
37
80
  raise 'not implemented!'
38
81
  end
39
82
  # :nocov:
40
83
 
84
+ # == Обработка завершения (абстрактный)
85
+ #
86
+ # @param status [Process::Status|nil] статус завершения
87
+ # @raise [RuntimeError] если не реализован в подклассе
41
88
  # :nocov:
42
89
  def reap(*_args)
43
90
  raise 'not implemented!'
44
91
  end
45
92
  # :nocov:
46
93
 
47
- # :nocov:
94
+ # == Публикация события
95
+ #
96
+ # Отправляет событие в канал событий диспетчера.
97
+ #
98
+ # @param event [String, Symbol] событие для отправки
48
99
  def publish(event)
49
100
  dispatcher.bus.puts(event)
50
101
  end
51
- # :nocov:
52
102
 
103
+ # == Установить обратный вызов терминации
104
+ #
105
+ # @param block [Proc] блок кода, который будет вызван при терминации
53
106
  def on_term &block
54
107
  @on_term = block
55
108
  end
56
109
 
110
+ # == Проверка завершения
111
+ #
112
+ # @return [Boolean] true если обработчик завершен
57
113
  # :nocov:
58
114
  def finished?
59
115
  @finished
60
116
  end
61
117
  # :nocov:
62
118
 
119
+ # == Проверка успешного завершения
120
+ #
121
+ # @return [Boolean] true если завершен и успешно
63
122
  # :nocov:
64
123
  def success?
65
124
  finished? && @success
66
125
  end
67
126
  # :nocov:
68
127
 
128
+ # == Проверка запущенности
129
+ #
130
+ # @return [Boolean] true если обработчик работает
69
131
  # :nocov:
70
132
  def running?
71
133
  !finished?
72
134
  end
73
135
  # :nocov:
74
136
 
137
+ # == Проверка терминации
138
+ #
139
+ # @return [Time|nil] момент начала терминации или nil
75
140
  # :nocov:
76
141
  def terminating?
77
142
  @terminating_at
78
143
  end
79
144
  # :nocov:
80
145
 
146
+ # == Логика повторов
147
+ #
148
+ # Управляет повторами после завершения:
149
+ # - :unlimited — бесконечные повторы
150
+ # - Integer >= 0 — декремент и повтор
151
+ # - иначе — отправляет term через bus
152
+ #
153
+ # @return void
81
154
  def handle_retry
82
155
  if @retry_count == :unlimited
83
156
  logger.info "#{@handler_type}[#{name}] retry...."
@@ -89,7 +162,5 @@ module MainLoop
89
162
  publish(:term)
90
163
  end
91
164
  end
92
-
93
165
  end
94
166
  end
95
-
@@ -2,13 +2,49 @@ require 'logger'
2
2
  require 'timeouter'
3
3
 
4
4
  module MainLoop
5
-
5
+ # Сигналы для терминации
6
+ # @return [Array<String>]
6
7
  TERM_SIGNALS = %w[INT TERM].freeze
7
8
 
8
- class Loop
9
+ # = MainLoop::Loop
10
+ #
11
+ # Главный цикл управления, запускает обработку сигналов, обрабатывает события из Bus.
12
+ #
13
+ # == Жизненный цикл
14
+ #
15
+ # 1. {#run} устанавливает {#install_signal_handlers}
16
+ # 2. {#start_loop_forever} запускает цикл обработки событий
17
+ # 3. События из Bus обрабатываются через case:
18
+ # - 'term' → {#term}
19
+ # - 'crash' → {#crash}
20
+ # - /sig:/ → {#signal}
21
+ # - /reap:/ → {#reap}
22
+ # - nil → reap_children (timeout)
23
+ # 4. {Dispatcher#reap} получает завершенные процессы
24
+ # 5. {Dispatcher#tick} проверяет необходимость принудительного завершения
25
+ #
26
+ # == Пример использования
27
+ #
28
+ # bus = MainLoop::Bus.new
29
+ # dispatcher = MainLoop::Dispatcher.new(bus, timeout: 10)
30
+ # loop = MainLoop::Loop.new(bus, dispatcher)
31
+ #
32
+ # loop.run(30) # запуск с таймаутом 30 секунд
33
+ #
34
+ # == См. также
35
+ # - {MainLoop::Bus} — канал событий
36
+ # - {MainLoop::Dispatcher} — координирует обработчики
37
+ # - {MainLoop::ProcessHandler} — обработчики процессов
38
+ # - {MainLoop::ThreadHandler} — обработчики потоков
9
39
 
40
+ class Loop
10
41
  attr_reader :logger
11
42
 
43
+ # == Инициализация
44
+ #
45
+ # @param bus [Bus] канал событий
46
+ # @param dispatcher [Dispatcher] диспетчер обработчиков
47
+ # @param logger [Logger] логгер (по умолчанию Logger.new(nil))
12
48
  def initialize(bus, dispatcher, logger: nil)
13
49
  STDOUT.sync = true
14
50
  STDERR.sync = true
@@ -17,6 +53,12 @@ module MainLoop
17
53
  @logger = logger || Logger.new(nil)
18
54
  end
19
55
 
56
+ # == Запуск цикла
57
+ #
58
+ # Устанавливает обработчики сигналов и запускает {#start_loop_forever}.
59
+ #
60
+ # @param timeout [Numeric] таймаут цикла в секундах (0 = бесконечный)
61
+ # @raise [StandardError] если произошла ошибка в цикле
20
62
  def run(timeout = 0)
21
63
  install_signal_handlers(@bus)
22
64
 
@@ -28,9 +70,18 @@ module MainLoop
28
70
  # :nocov:
29
71
  end
30
72
 
31
- # TODO поскольку wait всегда равен 5 секунд, то цикл работы 5 секунд, и потому
32
- # timeout для Dispatcher нужно ставить больше 2 циклов, чтобы успели завершиться все потоки или процессы
73
+ # == Главный цикл обработки событий
74
+ #
75
+ # Цикл с ограниченным временем работы (через Timeouter).
76
+ #
77
+ # Интервал ожидания событий:
78
+ # wait = [[(timeout / 2.5), 5].min, 5].max
79
+ # Минимум 5 секунд (даже при timeout = 0)
80
+ #
81
+ # @param timeout [Numeric] таймаут цикла в секундах (0 = бесконечный)
33
82
  def start_loop_forever(timeout = 0)
83
+ # TODO поскольку wait всегда равен 5 секунд, то цикл работы 5 секунд, и потому
84
+ # timeout для Dispatcher нужно ставить больше 2 циклов, чтобы успели завершиться все потоки или процессы
34
85
  wait = [[(timeout / 2.5), 5].min, 5].max
35
86
  Timeouter.loop(timeout) do
36
87
  event = @bus.gets(wait)
@@ -56,6 +107,11 @@ module MainLoop
56
107
  end
57
108
  end
58
109
 
110
+ # == Установка обработчиков сигналов
111
+ #
112
+ # Устанавливает trap для TERM, INT и CLD.
113
+ # Сигналы отправляются в Bus через отдельные потоки.
114
+ #
59
115
  # :nocov:
60
116
  def install_signal_handlers(bus)
61
117
  TERM_SIGNALS.each do |sig|
@@ -70,6 +126,9 @@ module MainLoop
70
126
  end
71
127
  # :nocov:
72
128
 
129
+ # == Обработка сигнала
130
+ #
131
+ # @param command [String] команда вида "sig:NAME"
73
132
  def signal(command)
74
133
  _, sig = command.split(':')
75
134
  logger.debug("signal:#{sig}")
@@ -83,19 +142,73 @@ module MainLoop
83
142
  end
84
143
  end
85
144
 
145
+ # == Инициировать терминацию
146
+ #
147
+ # Передает команду терминации диспетчеру (если не уже терминация).
148
+ #
149
+ # @param _command [String] команда (Unused)
86
150
  def term(_command)
87
151
  @dispatcher.term unless @dispatcher.terminating?
88
152
  end
89
153
 
154
+ # == Отправить сигнал аварийного завершения
155
+ #
156
+ # Передает команду crash диспетчеру.
157
+ #
158
+ # @param _command [String] команда (Unused)
90
159
  def crash(_command)
91
160
  @dispatcher.crash
92
161
  end
93
162
 
163
+ # == Обработка завершения процесса
164
+ #
165
+ # Парсит команду "reap:id:status" и отправляет в диспетчер.
166
+ #
167
+ # @param command [String] команда вида "reap:id:status"
94
168
  def reap(command)
95
169
  _, id, status = command.split(':')
96
170
  @dispatcher.reap_by_id(id, status)
97
171
  end
98
172
 
173
+ # == Сбор завершенных процессов
174
+ #
175
+ # Проходит по всем PID обработчиков и собирает их статусы через wait2.
176
+ # Дополнительно собирает все оставшиеся дочерние процессы (wait2(-1)).
177
+ #
178
+ # == Особенности обработки ECHILD
179
+ #
180
+ # Если процесс завершился и был "съеден" другой системой (например, родительский процесс
181
+ # вызвал Process.wait в on_term обработчике), то Process.wait2(pid) вызовет Errno::ECHILD.
182
+ # Это нормальное поведение в Unix/Linux когда PID больше не существует в таблице процессов.
183
+ #
184
+ # В этом случае:
185
+ # - Мы добавляем [pid, nil] в результат, чтобы отметить, что процесс не найден
186
+ # - Обработка продолжается для остальных процессов в списке
187
+ # - Это предотвращает "зависание" обработки всех остальных процессов
188
+ #
189
+ # Пример сценария (см. test_process.rb):
190
+ # 1. ProcessHandler запускает процесс с PID 123
191
+ # 2. При терминации вызывается on_term(pid) в обработчике
192
+ # 3. on_term вызывает Process.wait(pid) и "съедает" статус
193
+ # 4. Позже reap_children пытается wait2(123) и получает ECHILD
194
+ # 5. Обработка продолжается для других процессов, а 123 помечается как [123, nil]
195
+ #
196
+ # == Логика обработки
197
+ #
198
+ # Метод проходит по каждому PID из @dispatcher.pids:
199
+ # - wait2(pid) возвращает [pid, status] если процесс найден
200
+ # - wait2(pid) возвращает nil если процесс еще не завершился
201
+ # - wait2(pid) вызывает Errno::ECHILD если PID уже не существует
202
+ #
203
+ # Для каждого случая:
204
+ # - Нам возвращается [pid, status] -> добавляем в results
205
+ # - Возвращается nil -> ничего не добавляем (не завершился)
206
+ # - ECHILD -> добавляем [pid, nil] (PID не найден, съеден другой системой)
207
+ #
208
+ # После обработки всех известных PID, делается wait2(-1) для сбора
209
+ # любых оставшихся дочерних процессов (с таймаутом 2 секунды).
210
+ #
211
+ # @return [Array<Array>] массив пар (pid, status)
99
212
  def reap_children
100
213
  results = []
101
214
 
@@ -105,6 +218,9 @@ module MainLoop
105
218
  results << result
106
219
  end
107
220
  rescue Errno::ECHILD
221
+ # Процесс "съеден" другой системой (например, Process.wait вызван в on_term)
222
+ # или процесс уже завершился и pid больше не существует
223
+ # Добавляем [pid, nil] чтобы отметить его и продолжить обработку остальных
108
224
  results << [pid, nil]
109
225
  end
110
226
  end
@@ -122,13 +238,16 @@ module MainLoop
122
238
  results
123
239
  end
124
240
 
241
+ # == Ожидание завершения процесса
242
+ #
243
+ # Обертка для Process.wait2 с флагом WNOHANG.
244
+ #
245
+ # @param pid [Integer] PID процесса для ожидания
246
+ # @return [Array<Integer, Process::Status>|nil] пара (pid, status) или nil если нет завершенных
125
247
  # :nocov:
126
248
  def wait2(pid)
127
249
  Process.wait2(pid, ::Process::WNOHANG)
128
250
  end
129
251
  # :nocov:
130
-
131
252
  end
132
-
133
253
  end
134
-
@@ -1,10 +1,47 @@
1
1
  require 'main_loop/handler'
2
2
 
3
+ # = MainLoop::ProcessHandler
4
+ #
5
+ # Управляет субпроцессами через Kernel.fork.
6
+ #
7
+ # == Жизненный цикл процесса
8
+ #
9
+ # 1. {#run} вызывает {#start_fork}
10
+ # 2. {#start_fork} создает fork с обработкой ошибок
11
+ # 3. process выполняется и завершается
12
+ # 4. {#reap(status)} получает статус завершения
13
+ # 5. {#handle_retry} решает, повторить или отправить term
14
+ #
15
+ # == Сигналы для процесса
16
+ #
17
+ # При {#term}:
18
+ # - Если не терминация: Process.kill('TERM', pid) + вызовы @runnable.on_term / @on_term
19
+ # - Если терминация: Process.kill('KILL', pid)
20
+ #
21
+ # == Пример использования
22
+ #
23
+ # MainLoop::ProcessHandler.new dispatcher, 'worker', retry_count: 3, logger: logger do
24
+ # sleep 2
25
+ # exit! 0
26
+ # end
27
+ #
28
+ # == См. также
29
+ # - {MainLoop::Handler} — базовый класс
30
+ # - {MainLoop::ThreadHandler} — обработчики потоков
31
+
3
32
  module MainLoop
4
33
  class ProcessHandler < MainLoop::Handler
5
-
6
34
  attr_reader :pid
7
35
 
36
+ # == Инициализация
37
+ #
38
+ # @param dispatcher [Dispatcher] ссылка на диспетчер
39
+ # @param name [String] имя обработчика
40
+ # @param runnable [Object] объект с методами run и on_term (опционально)
41
+ # @param retry_count [Integer, :unlimited] количество повторов
42
+ # @param logger [Logger] логгер
43
+ # @option runnable [Object] nil
44
+ # @option retry_count [Integer, :unlimited] 0
8
45
  def initialize(dispatcher, name, runnable: nil, **kwargs, &block)
9
46
  super
10
47
  @handler_type = 'Process'
@@ -23,10 +60,16 @@ module MainLoop
23
60
  run
24
61
  end
25
62
 
63
+ # == Получить PID процесса
64
+ #
65
+ # @return [Integer|nil] PID процесса или nil если не создан
26
66
  def id
27
67
  @pid
28
68
  end
29
69
 
70
+ # == Обработка завершения процесса
71
+ #
72
+ # @param status [Process::Status|nil] статус завершения или nil если неизвестен
30
73
  def reap(status)
31
74
  if status
32
75
  logger.info "Process[#{name}] exited: Pid:#{@pid} Status: #{status.exitstatus.inspect} Termsig: #{status.termsig.inspect} Success: #{status.success?}"
@@ -43,10 +86,13 @@ module MainLoop
43
86
  handle_retry
44
87
  end
45
88
 
46
- def term
89
+ # == Отправить сигнал терминации
90
+ #
91
+ # @param *_args (Unused)
92
+ def term(*_args)
47
93
  unless @pid
48
94
  @terminating_at ||= Time.now
49
- logger.debug "Process[#{name}] alredy terminated. Skipped."
95
+ logger.debug "Process[#{name}] already terminated. Skipped."
50
96
  return
51
97
  end
52
98
 
@@ -65,9 +111,12 @@ module MainLoop
65
111
  end
66
112
  end
67
113
 
68
- def kill
114
+ # == Принудительно завершить процесс
115
+ #
116
+ # @param *_args (Unused)
117
+ def kill(*_args)
69
118
  unless @pid
70
- logger.debug "Process[#{name}] alredy Killed. Skipped."
119
+ logger.debug "Process[#{name}] already Killed. Skipped."
71
120
  return
72
121
  end
73
122
 
@@ -76,6 +125,9 @@ module MainLoop
76
125
  ::Process.kill('KILL', @pid) rescue nil
77
126
  end
78
127
 
128
+ # == Запустить процесс
129
+ #
130
+ # Вызывает {#start_fork} с блоком или runnable.
79
131
  def run
80
132
  return if terminating?
81
133
 
@@ -86,6 +138,12 @@ module MainLoop
86
138
  end
87
139
  end
88
140
 
141
+ # == Fork-блок (protected)
142
+ #
143
+ # Создает дочерний процесс и настраивает обработку ошибок.
144
+ #
145
+ # @yield выполнить блок кода в дочернем процессе
146
+ # @return [Integer] PID дочернего процесса
89
147
  protected
90
148
 
91
149
  def start_fork
@@ -98,7 +156,5 @@ module MainLoop
98
156
  @finished = false
99
157
  logger.info "Process[#{name}] created: Pid:#{@pid}"
100
158
  end
101
-
102
-
103
159
  end
104
160
  end