-
Notifications
You must be signed in to change notification settings - Fork 46
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
RSDK-9591 - Add KillGroup to ManagedProcess #399
Changes from 1 commit
847140e
af8640d
6beca95
09c7703
9cf10a6
8ae366c
957ea62
fc2c2fa
1cbee8a
a9a013b
ffa8db3
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 |
---|---|---|
|
@@ -32,6 +32,10 @@ type ManagedProcess interface { | |
// there's any system level issue stopping the process. | ||
Stop() error | ||
|
||
// Kill will attempt to kill the process group and not wait for completion. Only use this if | ||
cheukt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// comfortable with leaking resources (in cases where exiting the program as quickly as possible is desired). | ||
Kill() | ||
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. Should we call this 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. updated |
||
|
||
// Status return nil when the process is both alive and owned. | ||
// If err is non-nil, process may be a) alive but not owned or b) dead. | ||
Status() error | ||
|
@@ -432,3 +436,29 @@ func (p *managedProcess) Stop() error { | |
} | ||
return errors.Errorf("non-successful exit code: %d", p.cmd.ProcessState.ExitCode()) | ||
} | ||
|
||
func (p *managedProcess) Kill() { | ||
// Minimally hold a lock here so that we can signal the | ||
// management goroutine to stop. We will attempt to kill the | ||
// process even if p.stopped is true. | ||
p.mu.Lock() | ||
if !p.stopped { | ||
close(p.killCh) | ||
p.stopped = true | ||
} | ||
|
||
if p.cmd == nil { | ||
p.mu.Unlock() | ||
return | ||
} | ||
p.mu.Unlock() | ||
|
||
// Since p.cmd is mutex guarded and we just signaled the manage | ||
// goroutine to stop, no new Start can happen and therefore | ||
// p.cmd can no longer be modified rendering it safe to read | ||
// without a lock held. | ||
// We are intentionally not checking the error here, we are already | ||
// in a bad state. | ||
//nolint:errcheck,gosec | ||
p.forceKill() | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,6 +4,7 @@ package pexec | |||||
|
||||||
import ( | ||||||
"os" | ||||||
"os/exec" | ||||||
"os/user" | ||||||
"strconv" | ||||||
"syscall" | ||||||
|
@@ -126,6 +127,14 @@ func (p *managedProcess) kill() (bool, error) { | |||||
return forceKilled, nil | ||||||
} | ||||||
|
||||||
// forceKill kills everything in the process group. This will not wait for completion and may result in a zombie process. | ||||||
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.
I have heard this term used in a couple different ways. What do you mean by "zombie process"? 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. the process will have finished but not waited on by the parent process - it is still in the process table. I'm explicitly not waiting on the child process here so the 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.
Suggested change
|
||||||
func (p *managedProcess) forceKill() error { | ||||||
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. Should we call this |
||||||
pidStr := strconv.Itoa(-p.cmd.Process.Pid) | ||||||
p.logger.Infof("killing entire process group %d", p.cmd.Process.Pid) | ||||||
benjirewis marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
//nolint:gosec | ||||||
return exec.Command("kill", "-9", pidStr).Start() | ||||||
} | ||||||
|
||||||
func isWaitErrUnknown(err string, forceKilled bool) bool { | ||||||
// This can easily happen if the process does not handle interrupts gracefully | ||||||
// and it won't provide us any exit code info. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,6 @@ func parseSignal(sigStr, name string) (syscall.Signal, error) { | |
return 0, errors.New("signals not supported on Windows") | ||
} | ||
|
||
|
||
func (p *managedProcess) sysProcAttr() (*syscall.SysProcAttr, error) { | ||
ret := &syscall.SysProcAttr{ | ||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, | ||
|
@@ -107,6 +106,13 @@ func (p *managedProcess) kill() (bool, error) { | |
return forceKilled, nil | ||
} | ||
|
||
// forceKill kills everything in the process tree. This will not wait for completion and may result in a zombie process. | ||
cheukt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
func (p *managedProcess) forceKill() error { | ||
pidStr := strconv.Itoa(p.cmd.Process.Pid) | ||
p.logger.Infof("force killing entire process tree %d", p.cmd.Process.Pid) | ||
return exec.Command("taskkill", "/t", "/f", "/pid", pidStr).Start() | ||
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. Did you test this on windows? 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. not yet, I just copied this from kill() a bit above |
||
} | ||
|
||
func isWaitErrUnknown(err string, forceKilled bool) bool { | ||
if !forceKilled { | ||
return false | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the story behind killing the process group? Is the
viam-server
part of the same process group? If we're intentionally trying to kill the viam-server + all modules running in one swift process groupkill -9
, what's the utility in adding this functionality to the goutils/managed process code?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the viam-server is not part of the same process group, each ManagedProcess is its own process group, I'm guessing the intention is to be able to
Kill
orStop
all spawned processes from one ManagedProcess at onceThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought* that was a possibility. But I didn't see the code that did that. Is that in our code or the SDK side?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
goutils/pexec/managed_process_unix.go
Line 69 in 8961a20