Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ lint: deps
golint -set_exit_status ./...

cover: deps
go get github.com/axw/gocov/gocov
goveralls

.PHONY: test deps lint cover
9 changes: 9 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: "{build}"
clone_folder: c:\gopath\src\github.com\Songmu\timeout
environment:
GOPATH: c:\gopath
build: false
test_script:
- go get -d -v -t ./...
- go tool vet -all .
- go test ./...
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
14 changes: 6 additions & 8 deletions timeout.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"os/exec"
"runtime"
"strconv"

"syscall"
"time"
Expand Down Expand Up @@ -104,7 +103,7 @@ func (tio *Timeout) signal() os.Signal {

// Run is synchronous interface of executing command and returning information
func (tio *Timeout) Run() (ExitStatus, string, string, error) {
cmd := tio.Cmd
cmd := tio.getCmd()
var outBuffer, errBuffer bytes.Buffer
cmd.Stdout = &outBuffer
cmd.Stderr = &errBuffer
Expand All @@ -120,7 +119,7 @@ func (tio *Timeout) Run() (ExitStatus, string, string, error) {

// RunSimple executes command and only returns integer as exit code. It is mainly for go-timeout command
func (tio *Timeout) RunSimple(preserveStatus bool) int {
cmd := tio.Cmd
cmd := tio.getCmd()

stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
Expand Down Expand Up @@ -168,7 +167,7 @@ func getExitCodeFromErr(err error) int {

// RunCommand is executing the command and handling timeout. This is primitive interface of Timeout
func (tio *Timeout) RunCommand() (chan ExitStatus, error) {
cmd := tio.Cmd
cmd := tio.getCmd()

if err := cmd.Start(); err != nil {
switch {
Expand Down Expand Up @@ -199,7 +198,7 @@ func (tio *Timeout) RunCommand() (chan ExitStatus, error) {
}

func (tio *Timeout) handleTimeout() (ex ExitStatus) {
cmd := tio.Cmd
cmd := tio.getCmd()
exitChan := getExitChan(cmd)
select {
case exitCode := <-exitChan:
Expand All @@ -215,9 +214,8 @@ func (tio *Timeout) handleTimeout() (ex ExitStatus) {
select {
case ex.Code = <-exitChan:
case <-time.After(tio.KillAfter):
if runtime.GOOS == "windows" {
exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run()
}
tio.killall()
// just to make sure
cmd.Process.Kill()
ex.Code = exitKilled
ex.typ = exitTypeKilled
Expand Down
10 changes: 5 additions & 5 deletions timeout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func TestPreserveStatus(t *testing.T) {
t.Skipf("skip test on Windows")
}
tio := &Timeout{
Cmd: exec.Command("perl", "test/exit_with_23.pl"),
Cmd: exec.Command("perl", "testdata/exit_with_23.pl"),
Duration: 1 * time.Second,
}

Expand All @@ -84,7 +84,7 @@ func TestPreserveStatus(t *testing.T) {

func TestKillAfter(t *testing.T) {
tio := &Timeout{
Cmd: exec.Command("perl", "test/ignore_sigterm.pl"),
Cmd: exec.Command("perl", "testdata/ignore_sigterm.pl"),
Signal: syscall.SIGTERM,
Duration: 1 * time.Second,
KillAfter: 1 * time.Second,
Expand All @@ -98,7 +98,7 @@ func TestKillAfter(t *testing.T) {

func TestKillAfterNotKilled(t *testing.T) {
tio := &Timeout{
Cmd: exec.Command("perl", "test/ignore_sigterm.pl"),
Cmd: exec.Command("perl", "testdata/ignore_sigterm.pl"),
Signal: syscall.SIGTERM,
Duration: 1 * time.Second,
KillAfter: 5 * time.Second,
Expand All @@ -116,7 +116,7 @@ func TestCommandCannotBeInvoked(t *testing.T) {
t.Skipf("skip test on Windows")
}
tio := &Timeout{
Cmd: exec.Command("test/dummy"),
Cmd: exec.Command("testdata/dummy"),
Duration: 1 * time.Second,
}
exit := tio.RunSimple(false)
Expand All @@ -132,7 +132,7 @@ func TestCommandNotFound(t *testing.T) {
t.Skipf("skip test on Windows")
}
tio := &Timeout{
Cmd: exec.Command("test/ignore_sigterm.pl-xxxxxxxxxxxxxxxxxxxxx"),
Cmd: exec.Command("testdata/ignore_sigterm.pl-xxxxxxxxxxxxxxxxxxxxx"),
Duration: 1 * time.Second,
}
exit := tio.RunSimple(false)
Expand Down
19 changes: 19 additions & 0 deletions timeout_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// +build !windows

package timeout

import (
"os/exec"
"syscall"
)

func (tio *Timeout) getCmd() *exec.Cmd {
if tio.Cmd.SysProcAttr == nil {
tio.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
return tio.Cmd
}

func (tio *Timeout) killall() error {
return syscall.Kill(-tio.Cmd.Process.Pid, syscall.SIGKILL)
}
20 changes: 20 additions & 0 deletions timeout_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package timeout

import (
"os/exec"
"strconv"
"syscall"
)

func (tio *Timeout) getCmd() *exec.Cmd {
if tio.Cmd.SysProcAttr == nil {
tio.Cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_UNICODE_ENVIRONMENT | 0x00000200,
}
}
return tio.Cmd
}

func (tio *Timeout) killall() error {
return exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(tio.Cmd.Process.Pid)).Run()
}