php editor Apple discovered that Python extensions written in Go may have problems when handling the Ctrl C signal. Normally, when we press Ctrl C on the command line, a SIGINT signal is sent to the running program to request it to stop execution. However, Python extensions written in Go seem to ignore this signal, causing the program to fail to stop gracefully. This issue is a potential hidden danger for developers writing Python extensions using Go and requires special attention. Therefore, during the development process, we should look for solutions to solve this problem to ensure the normal exit of the program.
I am using a python driver written in go that connects to a service and does some processing. We discovered an issue with an "unhealthy" instance of the service, which caused the driver to get stuck and unable to terminate unless we kill
the process.
Did some experiments and I found that when executing an extension written in go, the program ignores the ctrl c
command issued until control is returned to python, whereas with an extension written in c this does not happen This is the case where keyboardinterrupt
is thrown while executing c code. My question is why is this happening and if there is a way to circumvent this or issue some kind of timeout. Tried using threading.timer
to raise an exception to simulate a timeout, but the problem is that the exception is raised in it's own thread and does not interrupt the main thread. Tested in python (cpython) 3.9
and 3.10
. As for the POC extension I wrote, the go version is 1.20.4
, and the c compiler is 9.4.0
.
I'll leave a small poc below.
python code:
import ctypes import ctypes go_library = ctypes.cdll.loadlibrary('./go_library.so') hello_go = go_library.helloworld c_library = ctypes.cdll.loadlibrary("./c_library.so") hello_c = c_library.helloworld try: print("calling golang code") hello_go() print("calling c code") hello_c() except keyboardinterrupt: print("ctrl+c issued dd:") finally: print("done")
go extension
package main import ( "c" "log" "time" ) func helloworld(){ log.println("hello world") time.sleep(10 * time.second) log.println("done sleeping") } func main(){ }
c extension
#include <stdio.h> int helloWorld() { printf("Hello from C\n"); sleep(10); printf("Done sleeping from C\n"); return 0; }
I have also encountered the same situation. After some research I found the solution.
Based on this answer:
Python installs a signal handler on sigint which simply sets a flag that is checked by the main interpreter loop. In order for this handler to work properly, the Python interpreter must run the Python code.
It seems that when running golang code, golang calls the sigint signal handler installed by python to set the flag. Therefore, when running back to python, a keyboard interrupt is detected.
From golang documentation:
(when a non-go program calls go code) If notify is called for an asynchronous signal, a go signal handler is installed for the signal. If reset is later called on the signal, the original handling of the signal will be reinstalled, restoring the non-go signal handler (if any).
So the code below will capture sigint in the go code and return the function immediately on sigint.
func helloWorld() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT) // the original handling for that signal will be reinstalled, restoring the non-Go signal handler if any. defer signal.Reset(syscall.SIGINT) go func() { log.Println("Hello World") time.Sleep(10 * time.Second) log.Println("Done sleeping") c <- nil }() // wait go routine to finish or signal received <-c }
The above is the detailed content of Python extension written in Go ignores Ctrl+C. For more information, please follow other related articles on the PHP Chinese website!