Skip to content

Instantly share code, notes, and snippets.

@mexisme
Created September 16, 2017 07:19
Show Gist options
  • Save mexisme/d72addcb0d4d7cdd37b88d645b9699f8 to your computer and use it in GitHub Desktop.
Save mexisme/d72addcb0d4d7cdd37b88d645b9699f8 to your computer and use it in GitHub Desktop.
# Note: Updated from https://github.com/mexisme/spigot-docker/blob/master/lib/myshell.rb
# If your prefer the 'logging' gem:
# LOG = Logging.logger(STDOUT)
LOG = Logger.new(STDOUT)
require 'benchmark'
require 'timeout'
class MyShell
class Logged
def self.run(*args)
self.new(*args).call
end
attr_reader :cmd, :args
def initialize(cmd, *args)
@cmd = cmd
@args = args
end
def to_s
([cmd] + args).join(' ')
end
def inspect
to_s.inspect
end
def call
LOG.debug "Running #{inspect}"
Rake.sh cmd, *args
end
alias :sh :call
end
class Interruptible < Logged
class Interrupted < RuntimeError; end
KILL_TIMEOUT = 30
def call
status = nil
pid = Process.spawn(cmd, *args)
LOG.debug "Started #{cmd.inspect} with PID #{pid}."
begin
Signal.trap('INT') { raise Interrupted }
_, status = yield(self, pid) if block_given?
rescue Interrupted
Signal.trap('INT', 'DEFAULT')
raise
rescue => e
LOG.warn "An exception (#{e.inspect}) was raised while running #{cmd.inspect}."
end
LOG.error "#{cmd.inspect} failed with exit-code #{status}." if status && !status.success?
ensure
cleanup(pid)
end
def cleanup(pid)
catch(:done) do
# Step through a list of signal-types to kill the subprocess, until we kill it:
# TODO: Should we KILL ?
%w[INT TERM KILL KILL].each do |signal|
begin
LOG.debug "Sending #{signal.inspect} to #{cmd.inspect} (PID #{pid})."
Process.kill(signal, pid)
LOG.debug "Will wait #{KILL_TIMEOUT} sec for death..."
Timeout.timeout(KILL_TIMEOUT) do
Process.waitpid(pid)
throw :done
end
rescue Timeout::Error
LOG.debug "#{cmd.inspect} (PID #{pid}) didn't die within #{KILL_TIMEOUT} sec"
rescue SystemCallError
LOG.debug "PID #{pid} already gone..."
throw :done
end
end
fail "Could not kill PID #{pid}!"
end
end
end
class Benchmarked < Logged
def self.collected
@collected ||= []
end
def self.show_collected
collected.each do |sh|
elapsed = Time.at(sh.benchmark.real).utc.strftime("%-Hhr %-Mmin %-Ssec")
LOG.info "#{sh.benchmark.label} took #{elapsed}"
end
end
#####
attr_reader :benchmark
def initialize(*args)
super
end
def call
@benchmark = Benchmark.measure(inspect) { super }
self.class.collected << self
@benchmark
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment