I am using a python driver that's written in go, the driver connects to a certain service and do some processing. We found an issue with an "unheanlthy" instance of the service and that was making the driver to get stuck and was impossible to terminate unless we kill the process.
Doing some experimentation I saw that when an extension written in go is executed the program ignores the Ctrl+C commands issued until control comes back to Python, not happening this with a C written extension, in which the KeyboardInterrupt is raised while executing C code. My question is why this happens and if there's a way to circunvent this problem or issue a kind of timeout. Tried to raise an exception to emulate a timeout using the threading.Timer but the problem is that the exception is thrown in it's own thread and doesn't interrupt the main thread. Tested in Python (cpython) 3.9 and 3.10. As for the extensions I wrote as a poc 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 met same case. After some research, I found the solution.
According to this answer:
Python has a signal handler installed on SIGINT which simply sets a flag that is checked by the main interpreter loop. For this handler to work properly, the Python interpreter has to be running Python code.
It seems that when running golang code, the golang call the python installed SIGINT signal handler to set a flag. So when running back to python, a KeyboardInterrupt is deteced.
And from golang doc:
(When Non-Go programs that call Go code) If Notify is called for an asynchronous signal, a Go signal handler will be installed for that signal. If, later, Reset is called for that signal, the original handling for that signal will be reinstalled, restoring the non-Go signal handler if any.
So, the following code will catch SIGINT in go code and return the function immediately when 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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With