You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
syscall: misleading documentation for linux SysProcAttr.Pdeathsig #27505
Warning: the "parent" in this case is considered to be the thread that created this process. In other words, the signal will be sent when that thread terminates (via, for example, pthread_exit(3)), rather than after all of the threads in the parent process terminate.
I got bit by this in a python program -- started a new program on one thread, and tried to wait for it on another thread, and the child process kept dying and it took awhile to figure out what was going on. While I haven't ran into it in go yet, because in go threads and goroutines aren't one to one, I imagine if one ran into this sort of bug it would only occur intermittently.
Thinking about it, it seems like a user might want to call runtime.LockOSThread when using this?
It's not clear to me whether the docs should have a larger warning in them -- but I think at the minimum the documentation should be updated to say 'parent thread' or 'parent goroutine' instead of just 'parent'.
If I am not mistaken, the current implementation of Go never kills threads, which is why you wouldn't be able to run into this bug in Go currently. At the same time, if Go ever does start killing threads, this might hit a lot of people at once.
Looked around a bit, looks like go 1.10+ will kill a thread if you lock it and the goroutine exits without unlocking it: #20395
Here's an example that shows that:
package main
import (
"flag""fmt""os/exec""runtime""syscall"
)
funcmain() {
kill:=falseflag.BoolVar(&kill, "death", false, "Set deathsig")
flag.Parse()
cmd:=exec.Command("sleep", "1000")
ifkill==true {
fmt.Println("Death is coming")
cmd.SysProcAttr=&syscall.SysProcAttr{
Setpgid: true,
Pdeathsig: syscall.SIGKILL,
}
}
// force other goroutines to be spawned on a new threadruntime.LockOSThread()
done:=make(chanstruct{})
gofunc() {
runtime.LockOSThread()
err:=cmd.Start()
iferr!=nil {
fmt.Println(err)
}
close(done)
fmt.Println("Exit goroutine")
}()
<-donefmt.Println("Waiting")
err:=cmd.Wait()
fmt.Println("Done", err)
}
In that example, if -death is passed as an argument the program will immediately exit. Obviously, you have to go out of your way to trigger this behavior, but presumably someone who is messing with platform-specific behavior is going to want to know about platform-specific behaviors... thus why the docs should at least hint that this is possible.
changed the title [-]Update documentation for linux SysProcAttr.Pdeathsig[/-][+]syscall: misleading documentation for linux SysProcAttr.Pdeathsig[/+]on Sep 6, 2018
Circling back to this, I did eventually figure out the right way to use Pdeathsig, and I think the documentation should include some examples or discussion about this.
Here's an example of what people usually want to do when using Pdeathsig -- ensure the child dies when the parent dies. This example ensures that golang won't accidentally kill the OS thread associated with the child process.
package main
import (
"fmt""os/exec""runtime""syscall"
)
funcmain() {
done:=make(chanstruct{})
cmd:=exec.Command("sleep", "1000")
cmd.SysProcAttr=&syscall.SysProcAttr{
Setpgid: true,
Pdeathsig: syscall.SIGKILL,
}
gofunc() {
// On Linux, pdeathsig will kill the child process when the thread dies,// not when the process dies. runtime.LockOSThread ensures that as long// as this function is executing that OS thread will still be aroundruntime.LockOSThread()
deferruntime.UnlockOSThread()
err:=cmd.Start()
iferr!=nil {
fmt.Println(err)
}
fmt.Println("Child is PID", cmd.Process.Pid)
err=cmd.Wait()
close(done)
fmt.Println("Child exited:", err)
}()
fmt.Println("Waiting for child to exit")
<-donefmt.Println("Parent exiting")
}
Reacted by Fabian Holler, Matthias Kunnen, Jan Wrobel, Nilson Pontello and darylac
The CL says " A workaround is to call LockOSThread just before starting the new process and UnlockOSThread after it finishes." But the thread can then later, before the child process exits, pick up another goroutine, that goroutine can call LockOSThread, and then that goroutine can exit. That will cause the thread to exit, and cause a signal to be sent to the child process, although the parent process overall is still running. Unless I misunderstand something.
The idea is that the goroutine locks to thread, and then just waits and does nothing until the process exits. That way, the thread cannot exit until the exec'd process exits
gofunc() {
runtime.LockOSThread()
err:=cmd.Start()
iferr!=nil {
...
}
// some channels to communicate with this goroutine or whatever are here...// <-finishederr=cmd.Wait()
runtime.UnlockOSThread()
}()
Thanks, I understand the comment now. I was reading "after it finishes" as meaning "after StartProcess completes," but it really means "after Wait completes.`"
You might want to include does not work with darwin in the title.
The title of this issue explicitly says "linux", and Pdeathsig is defined in exec_linux.go. That seems clear enough?
You are omitting that Pdeathsig is defined for FreeBSD and OpenBSD.
I think there can be another issue but it is a very simple fix to document and add a Pdeathsig parameter that has no affect to the darwin specification considering that updating the documentation is going to require updating it for many other OS.
FreeBSD supports parent-death signal functionality natively via the procctl() system call using PROC_PDEATHSIG_CTL. This mirrors Linux's prctl(PR_SET_PDEATHSIG) feature, ensuring a designated signal is delivered to a child process if its parent process terminates. It is supported in standard programming languages like Go (via SysProcAttr.Pdeathsig.
You can also read #46258 and discover that FreeBSD support was already proposed, accepted and completed: 2feb2cc meaning that the documentation saying linux only needs to be fixed and consider this.
You are omitting that Pdeathsig is defined for FreeBSD and OpenBSD.
This issue is about misleading documentation for Pdeathsig for Linux. If the documentation for FreeBSD/OpenBSD is also misleading, it seems better to open a different issue to keep this one focused on what it was originally intended to be about.
In any case, I think the Linux documentation is sufficient now, so this issue could probably be closed.
You are omitting that Pdeathsig is defined for FreeBSD and OpenBSD.
This issue is about misleading documentation for Pdeathsig for Linux. If the documentation for FreeBSD/OpenBSD is also misleading, it seems better to open a different issue to keep this one focused on what it was originally intended to be about.
In any case, I think the Linux documentation is sufficient now, so this issue could probably be closed.
I haven't seen any job postings of Issue manager meaning that what you are saying has an invalid premise without proper employment.
I agree that it should be a case by case basis, but unfortunately honesty isn't respected when there's a period of learning (GPU) and many people use pattern matching during those times to manipulate the lazy in order that their profit is consolidated.
Activity
dominikh commentedon Sep 5, 2018
If I am not mistaken, the current implementation of Go never kills threads, which is why you wouldn't be able to run into this bug in Go currently. At the same time, if Go ever does start killing threads, this might hit a lot of people at once.
virtuald commentedon Sep 5, 2018
Looked around a bit, looks like go 1.10+ will kill a thread if you lock it and the goroutine exits without unlocking it: #20395
Here's an example that shows that:
In that example, if
-deathis passed as an argument the program will immediately exit. Obviously, you have to go out of your way to trigger this behavior, but presumably someone who is messing with platform-specific behavior is going to want to know about platform-specific behaviors... thus why the docs should at least hint that this is possible.[-]Update documentation for linux SysProcAttr.Pdeathsig[/-][+]syscall: misleading documentation for linux SysProcAttr.Pdeathsig[/+]virtuald commentedon Oct 21, 2020
Circling back to this, I did eventually figure out the right way to use Pdeathsig, and I think the documentation should include some examples or discussion about this.
Here's an example of what people usually want to do when using Pdeathsig -- ensure the child dies when the parent dies. This example ensures that golang won't accidentally kill the OS thread associated with the child process.
gopherbot commentedon Jun 14, 2022
Change https://go.dev/cl/412114 mentions this issue:
syscall: clarify Pdeathsig documentation on Linuxcespare commentedon Jun 14, 2022
@ianlancetaylor On the CL you wrote
(The workaround being to use LockOSThread in the goroutine that creates the child process to ensure that that thread can't be killed.)
Can you say more about why it might not work?
ianlancetaylor commentedon Jun 14, 2022
The CL says " A workaround is to call LockOSThread just before starting the new process and UnlockOSThread after it finishes." But the thread can then later, before the child process exits, pick up another goroutine, that goroutine can call LockOSThread, and then that goroutine can exit. That will cause the thread to exit, and cause a signal to be sent to the child process, although the parent process overall is still running. Unless I misunderstand something.
cespare commentedon Jun 15, 2022
Isn't the point of LockOSThread that this cannot happen?
Tasssadar commentedon Jun 15, 2022
The idea is that the goroutine locks to thread, and then just waits and does nothing until the process exits. That way, the thread cannot exit until the exec'd process exits
syscall: clarify Pdeathsig function on Linux
ianlancetaylor commentedon Jun 15, 2022
Thanks, I understand the comment now. I was reading "after it finishes" as meaning "after
StartProcesscompletes," but it really means "afterWaitcompletes.`"25 remaining items
andrewhodel commentedon Aug 12, 2026
You are omitting that
Pdeathsigis defined for FreeBSD and OpenBSD.I think there can be another issue but it is a very simple fix to document and add a
Pdeathsigparameter that has no affect to thedarwinspecification considering that updating the documentation is going to require updating it for many other OS.You can also read #46258 and discover that FreeBSD support was already proposed, accepted and completed: 2feb2cc meaning that the documentation saying
linux onlyneeds to be fixed and consider this.virtuald commentedon Aug 12, 2026
This issue is about misleading documentation for Pdeathsig for Linux. If the documentation for FreeBSD/OpenBSD is also misleading, it seems better to open a different issue to keep this one focused on what it was originally intended to be about.
In any case, I think the Linux documentation is sufficient now, so this issue could probably be closed.
andrewhodel commentedon Aug 12, 2026
I haven't seen any job postings of Issue manager meaning that what you are saying has an invalid premise without proper employment.
I agree that it should be a case by case basis, but unfortunately honesty isn't respected when there's a period of learning (GPU) and many people use pattern matching during those times to manipulate the lazy in order that their profit is consolidated.
feat(parentwatch): cancel a context when the parent process exits
fix(materialize): a worker never outlives its supervising process (TA…
fix(materialize): a worker never outlives its supervising process (TA…
feat(materialize): worker supervisor with per-OS memory cap (TASK-219…
Stop relying on Pdeathsig and go-plugin's managed clients
Item 251: Xvfb dies with the process that started it, so a killed hea…
fix(session): stop ssh sandbox tunnels when the daemon exits (#5564) (#…
libcontainer: stage a container init into a PID namespace by path
feat(multiplex): child LSPs do not outlive their daemon
Leave no plugin running when entire dies