-
Notifications
You must be signed in to change notification settings - Fork 266
Kill replaced worker processes when interrupted #370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
babb0da
7fd3772
559643c
4fbaa37
34ea3f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -169,24 +169,44 @@ class UserInterruptHandler | |
|
|
||
| class << self | ||
| # kill all these pids or threads if user presses Ctrl+c | ||
| def kill_on_ctrl_c(pids, options) | ||
| def kill_on_ctrl_c(workers, options) | ||
| @to_be_killed ||= [] | ||
| old_interrupt = nil | ||
| signal = options.fetch(:interrupt_signal, INTERRUPT_SIGNAL) | ||
|
|
||
| if @to_be_killed.empty? | ||
| old_interrupt = trap_interrupt(signal) do | ||
| # Wrap the existing interrupt handler to kill the workers first. Workers may have been replaced, | ||
| # so get the latest pids. | ||
| # 1. The worker arrays in @to_be_killed are protected by options[:mutex]. | ||
| # 2. Mutexes cannot be obtained in a trap context. | ||
| # 3. To preserve semantics, the workers must be killed before the old handler runs. | ||
| # 4. To preserve semantics, the old handler must run in a trap context on the main thread. | ||
| kill_thread = nil | ||
| old_interrupt = Signal.trap(signal) do | ||
| next if kill_thread | ||
| warn 'Parallel execution interrupted, exiting ...' | ||
| @to_be_killed.flatten.each { |pid| kill(pid) } | ||
| end | ||
| kill_thread = Thread.new do | ||
| pids = options[:mutex].synchronize do | ||
| @to_be_killed.flatten(1).map(&:pid) | ||
| # FUTURE: stop JobFactory from spawning new workers | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing this properly would involve a new |
||
| end | ||
| pids.each { |pid| kill(pid) } | ||
| if old_interrupt == "DEFAULT" | ||
| Signal.trap(signal) { raise Interrupt } | ||
| else | ||
| Signal.trap(signal, old_interrupt) | ||
| end | ||
| Process.kill(signal, Process.pid) # run the old interrupt handler | ||
| end | ||
| end || "DEFAULT" | ||
| end | ||
|
|
||
| @to_be_killed << pids | ||
| @to_be_killed << workers | ||
|
|
||
| yield | ||
| ensure | ||
| @to_be_killed.pop # do not kill pids that could be used for new processes | ||
| restore_interrupt(old_interrupt, signal) if @to_be_killed.empty? | ||
| Signal.trap(signal, old_interrupt) if @to_be_killed.empty? # restore the old handler on our way out | ||
| end | ||
|
|
||
| def kill(thing) | ||
|
|
@@ -195,27 +215,6 @@ def kill(thing) | |
| # some linux systems already automatically killed the children at this point | ||
| # so we just ignore them not being there | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def trap_interrupt(signal) | ||
| old = Signal.trap signal, 'IGNORE' | ||
|
|
||
| Signal.trap signal do | ||
| yield | ||
| if !old || old == "DEFAULT" | ||
| raise Interrupt | ||
| else | ||
| old.call | ||
| end | ||
| end | ||
|
|
||
| old | ||
| end | ||
|
|
||
| def restore_interrupt(old, signal) | ||
| Signal.trap signal, old | ||
| end | ||
| end | ||
| end | ||
|
|
||
|
|
@@ -557,7 +556,7 @@ def work_in_processes(job_factory, options, &blk) | |
| results_mutex = Mutex.new # arrays are not thread-safe | ||
| exception = nil | ||
|
|
||
| UserInterruptHandler.kill_on_ctrl_c(workers.map(&:pid), options) do | ||
| UserInterruptHandler.kill_on_ctrl_c(workers, options) do | ||
| in_threads(options) do |i| | ||
| worker = workers[i] | ||
| worker.thread = Thread.current | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # frozen_string_literal: true | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some description here what this is doing/simulating
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||
| require './spec/cases/helper' | ||
|
|
||
| parent_pid = Process.pid | ||
| killer_pid = fork do | ||
| sleep 1 | ||
| Process.kill(:INT, parent_pid) | ||
| end | ||
| Process.detach(killer_pid) | ||
|
|
||
| Parallel.each([0.1, 5], in_processes: 1, isolation: true) do |sec| | ||
| sleep sec | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.