Skip to content

Commit

Permalink
Fix interrupt handling in Bundler workers
Browse files Browse the repository at this point in the history
SharedHelpers.trap failed when the previous handler for a signal is not callable
(for example, when it is the string "DEFAULT").

Instead, we now handle interrupts by aborting the process when worker threads are
running, and restore the previous handler after worker threads are finished.

Fixes rubygems#4764.
  • Loading branch information
haines committed Jul 15, 2021
1 parent e7bf6b4 commit 0f05125
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 9 deletions.
7 changes: 0 additions & 7 deletions bundler/lib/bundler/shared_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,6 @@ def print_major_deprecations!
Bundler.ui.warn message
end

def trap(signal, override = false, &block)
prior = Signal.trap(signal) do
block.call
prior.call unless override
end
end

def ensure_same_dependencies(spec, old_deps, new_deps)
new_deps = new_deps.reject {|d| d.type == :development }
old_deps = old_deps.reject {|d| d.type == :development }
Expand Down
16 changes: 14 additions & 2 deletions bundler/lib/bundler/worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def initialize(size, name, func)
@func = func
@size = size
@threads = nil
SharedHelpers.trap("INT") { abort_threads }
end

# Enqueue a request to be executed in the worker pool
Expand Down Expand Up @@ -68,13 +67,16 @@ def apply_func(obj, i)
# so as worker threads after retrieving it, shut themselves down
def stop_threads
return unless @threads

@threads.each { @request_queue.enq POISON }
@threads.each(&:join)

remove_interrupt_handler

@threads = nil
end

def abort_threads
return unless @threads
Bundler.ui.debug("\n#{caller.join("\n")}")
@threads.each(&:exit)
exit 1
Expand All @@ -94,11 +96,21 @@ def create_threads
end
end.compact

add_interrupt_handler unless @threads.empty?

return if creation_errors.empty?

message = "Failed to create threads for the #{name} worker: #{creation_errors.map(&:to_s).uniq.join(", ")}"
raise ThreadCreationError, message if @threads.empty?
Bundler.ui.info message
end

def add_interrupt_handler
@previous_interrupt_handler = trap("INT") { abort_threads }
end

def remove_interrupt_handler
trap "INT", @previous_interrupt_handler
end
end
end

0 comments on commit 0f05125

Please sign in to comment.