Go preempt can exit out of a loop [duplicate] - go

This question already has answers here:
At which point a goroutine can yield?
(2 answers)
Closed 9 months ago.
I am trying to understand how "go" can preempt a loop or blocking call. I understand that with SIGURG signal, the blocking surbroutine stack is poisoned and a JMP is made to preempt function. However when the subroutine is given a chance again to execute, how can it exit the loop as is mentioned in the article https://developpaper.com/goroutine-preemptive-scheduling-with-new-features-of-go-1-14/
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
runtime.GOMAXPROCS(1)
fmt.Println("The program starts ...")
go func() {
for {
}
}()
time.Sleep(time.Second)
fmt.Println("I got scheduled!")
}
With go 1.13, the program hangs forever
$ docker run -it --rm app13:latest
The program starts ...
With go 1.14, the loop exits?
$ docker run -it --rm app14:latest
The program starts ...
I got scheduled!
Is there any good documentation, paper i can read to understand this behavior. To me this looks like a bug, a loop is a loop, how can it exit irrespective it is prempt or not.

The Go Language Specification says:
Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.
Preemption in Go 1.14 and later ensures that the goroutine executing the main function runs and that that main function returns. The program exits when the main function returns. Any running goroutines cease to exist when the program exits.
In the absence of preemption, the goroutine executing the main function may not run. The program hangs if the main function does not return.

Related

VS code restarts when an infinite loop runs in Golang but not for python

I have a program that runs an infinite loop:
func main(){
fmt.Println("welcome to Goalang World!")
for i:=0;true;i++{
fmt.Println("1: ", "g")
}
}
If I run this code then VS code terminates with below message in terminal:
Restarting the terminal because the connection to the shell process was lost.
But no issue when running infinite loop in python.
Do i need to change any terminal settings in my VScode?
Note: Same code works fine in https://go.dev/play/

Does a goroutine terminates after its Kubernetes container gets terminated?

For example, a container runs the main thread and a goroutine. The main thread encounters an issue and terminates. Note that for Golang, termination of the main thread does not result in auto-termination of the goroutine.
As the main thread has been terminated, will the container be killed and re-created? Or will the container continue running due to the goroutine is still running?
If the container will be killed and re-created after the main thread has been terminated, will this result in the goroutine getting terminated as well? Or will the goroutine continue running indefinitely and there is no easy way to terminate it now?
If the main functions exists, the program is stopped. Nothing will run any more. It will release any used resource, like file descriptors and database connections.
In the below program, we will never see done being printed.
func main() {
go func() {
time.Sleep(time.Minute)
fmt.Println("done")
}()
time.Sleep(time.Second * 3)
}
https://play.golang.com/p/kPKZDdMcduS
If the program with that main function was the foreground process of the container, then the container shuts down as its standard behaviour with containers.
If you run the below example, you can observe how the container shuts down as soon as the sleep finishes.
$ docker run --name sample busybox sleep 3 && docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd4319261a0d busybox "sleep 3" 4 seconds ago Exited (0) Less than a second ago sample
If the container, the program is running in, is shut down, it's more or less as if you would pull the plug of your computer. Nothing will run on your computer any more. It's impossible.
I would encourage you to create some test scenarios yourself and validate this.

Terminate shell pipe from interactive go cli

I have a Go program that consumes "live" input from a shell pipe, eg:
tail -f some/file | my-program
my-program is an interactive program built with rivo/tview. I want to be able to close my program with Ctrl-C and have it also terminate the tail -f that supplies input to it.
Currently I have to hit Ctrl-C twice to get back to my shell prompt. Any way I can get back to my prompt by hitting Ctrl-C once?
Adjusted my program per #torek's explanation of progress groups and observation that I can get the progress group ID using unix.Getpgid(pid):
import (
"os"
"golang.org/x/sys/unix"
)
func main() {
// do stuff with piped input
pid := os.Getpid()
pgid, err := unix.Getpgid(pid)
if err != nil {
log.Fatalf("could not get process group id for pid: %v\n", pid)
}
processGroup, err := os.FindProcess(pgid)
if err != nil {
log.Fatalf("could not find process for pid: %v\n", pgid)
}
processGroup.Signal(os.Interrupt)
}
This delivers my desired behavior from my original question.
I opted to not use syscall because of the warning I found:
Deprecated: this package is locked down. Callers should use the corresponding package in the golang.org/x/sys repository instead. That is also where updates required by new systems or versions should be applied. See https://golang.org/s/go1.4-syscall for more information.
I plan to update my program to detect whether or not it was given a pipe using the strategy outlined in this article, so when a pipe is detected, I'll do the above process group signaling on interrupt.
Any issues with that?
We'll assume a Unix-like system, using a shell that understands and engages in job control (and they all do now). When you run a command, the shell creates something called a process group or "pgroup" to hold each of the processes that make up the command. If the command is a pipeline (as this one is), each process in the pipeline gets the same pgroup-ID (see setpgid).
If the command is run in the forgeground (without &), the controlling terminal has this particular pgid assigned to it. Pressing one of the signal-generating keys, such as CTRL-C or CTRL-\, sends the corresponding signal (SIGINT and SIGQUIT in these cases) to the pgroup, using an internal killpg or equivalent. This sends the signal to every member of the pgroup.
(Backgrounding a process is simply *cough* a matter of taking back the pgid on the controlling tty, then restarting the processes in the pipeline. To make that happen is not so simple, though, as indicated by the "restarting" here.)
The likely source of the problem here is that an interactive program will place the controlling terminal into cbreak or raw mode and disable some or all signalling from keyboard keys, so that, for instance, CTRL-C no longer causes the kernel's tty module to send a signal at all. Instead, if you see a key that should cause suspension (CTRL-Z) or termination, the program has to do its own suspending or terminating. Programmers sometimes assume that this consists of simply suspending or terminating—but since the entire pipeline never got the signal in question, that's not the case, unless the entire shell pipeline consisted solely of the interactive program.
The fix is to have the program send the signal to its own pgroup, after doing any necessary cleanup (temporarily or permanently) of the controlling terminal.

Detect zombie child process

My golang program starts a service program which is supposed to run forever, like this:
cmd := exec.Command("/path/to/service")
cmd.Start()
I do NOT want to wait for the termination of "service" because it is supposed to be running forever. However, if service starts with some error (e.g. it will terminate if another instance is already running), the child process will exit and become zombie.
My question is, after cmd.Start(), can I somehow detect if the child process is still running, rather than becomes a zombie? The preferred way might be:
if cmd.Process.IsZombie() {
... ...
}
or,
procStat := cmd.GetProcessStatus()
if procStat.Zombie == true {
... ...
}
i.e. I hope there are some way to get the status of a (child) process without waiting for its exit code, or, to "peek" its status code without blocking.
Thanks!
Judging from the docs the only way to get the process state is to call os.Process.Wait. So it seems you will have to call wait in a goroutine, and then you can easily check if that goroutine has exited yet:
var cmd exec.Cmd
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case err := <-done:
// inspect err to check if service exited normally
default:
// not done yet
}
The best solution (for me) is:
add a signal handler listen for SIGCHLD
on receiving SIGCHLD, call cmd.Wait()
This way, the zombie process will disappear.

How to verify that parent process has exited in golang on Windows?

So I am writing a small utility which should be able to update itself (replace it's own binary).
The best way to do this on Windows seems to be:
Download the new version of the binary as my.exe.new
my.exes runs my.exe.new and exits
my.exe.new waits for my.exe to exit
my.exe.new copies itself as my.exe
my.exe.new starts another copy of itself as my.exe and exits
my.exe waits for my.exe.new to exit
my.exe removes my.exe.new
Now for all of this to work I have to be able to synchronize the state between the processes (being able to know when the parent has exited), but it seems that os.Getppid (nor syscall.Getppid) in golang Windows is not implemented as it always returns -1.
I've seen that patches are underway, but I am not willing to patch my standard libraries.
Is there an easy way to make Getppid working on even older versions of Go (perhaps reimplementing it?), or perhaps anyone can suggest a better method of synchronizing between the process state?
The thing which comes to mind is binding on a socket, but thats a big hacky.
Perhaps passing a pipe to the child process, and the child waiting for the pipe to close?
Thanks
You could use os.Stdin with exec.Cmd, now I don't have access to windows to test this, however the same concept should apply there just fine:
var child = flag.Bool("child", false, "damn children and their music")
func init() {
flag.Parse()
}
func main() {
if *child {
fmt.Println("child start", time.Now())
// wait until the parent dies and bufio closes the stdin
ioutil.ReadAll(os.Stdin)
fmt.Println("the parent is dead", time.Now())
time.Sleep(5 * time.Second)
fmt.Println("tada\n")
} else {
fmt.Fprintln(os.Stdout, time.Now())
cmd := exec.Command(os.Args[0], "-child")
cmd.Stdout = os.Stdout //not needed in a real program.
//this is important, bufio will close after the parent exits,
// unlike os.Stdin which screws up, at least on linux
cmd.Stdin = bufio.NewReader(os.Stdin)
fmt.Println("giving painful birth:", cmd.Start())
time.Sleep(2 * time.Second)
}
}
The parent process can pass its PID to the child.
You could use a command-line parameter or an environment variable to do this.

Resources