Skip to content

Commit

Permalink
implement timeout option with SIGKILL
Browse files Browse the repository at this point in the history
fixes #1
  • Loading branch information
Eric Mueller committed Jul 1, 2015
1 parent 45be675 commit 5ef9412
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ IsoLatte.fork(

### Options

* `:stderr` - A path to write the subprocess' stderr stream into. Defaults to '/dev/null',
* `stderr` - A path to write the subprocess' stderr stream into. Defaults to '/dev/null',
supplying `nil` lets the subprocess continue writing to the parent's stderr stream.
* `success` - a callable to execute if the subprocess completes successfully.
* `fault` - a callable to execute if the subprocess receives a SIGABRT (segfault).
Expand All @@ -45,6 +45,8 @@ IsoLatte.fork(
Receives the exit status value as its argument.
* `finish` - a callable to execute when the subprocess terminates in any way. It receives
a boolean 'success' value and an exit status as its arguments.
* `timeout` - a number of seconds to wait - if the process has not terminated by then,
the parent will kill it by issuing a SIGKILL signal (triggering the kill callback)

## Roadmap

Expand Down
21 changes: 20 additions & 1 deletion lib/iso_latte.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "ostruct"
require "timeout"

module IsoLatte
NO_EXIT = 122
Expand All @@ -16,6 +17,7 @@ module IsoLatte
# fault: a callable to execute if the subprocess segfaults, core dumps, etc.
# exit: a callable to execute if the subprocess voluntarily exits with nonzero.
# receives the exit status value as its argument.
# timeout: after this many seconds, the parent should send a SIGKILL to the child.
#
# It is allowable to Isolatte.fork from inside an IsoLatte.fork block (reentrant)
#
Expand Down Expand Up @@ -59,7 +61,17 @@ def self.fork(options = nil, &block)

write_ex.close

pid, rc = Process.wait2(child_pid)
pid, rc =
begin
if opts.timeout
Timeout.timeout(opts.timeout) { Process.wait2(child_pid) }
else
Process.wait2(child_pid)
end
rescue Timeout::Error
kill_child(child_pid)
end

fail(Error, "Wrong child's exit received!") unless pid == child_pid

if rc.exited? && rc.exitstatus == EXCEPTION_RAISED
Expand Down Expand Up @@ -91,5 +103,12 @@ def self.fork(options = nil, &block)
rc
end

def self.kill_child(pid)
Process.kill("KILL", pid)
Process.wait2(pid)
rescue Errno::ESRCH
# Save us from the race condition where it exited just as we decided to kill it.
end

class Error < StandardError; end
end

0 comments on commit 5ef9412

Please sign in to comment.