-
-
Notifications
You must be signed in to change notification settings - Fork 9.5k
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
ENH: f2py
wrappers should respect SIGINT (CTRL-C)
#20148
Comments
Note that in other instances subroutine main
implicit none
integer :: i
do i = 1, 10
print '(a)', 'zzz ...'
call sleep(2)
end do
end subroutine main This, with |
As far as I know, there is no solution for this, except checking for interrups regularly using the Python API (and there is no good API exposed to do that if you want to release the GIL right now). So while correct, this is just how things are. NumPy doesn't check for keyboard interrupts either. (There used to be code in |
I'm not sure I fully understand, the problem here is that the |
@HaoZeke did you try running the fortran code for a long time rather than in an infinite loop? I assume the Python interrupt handler gets called even if you are "inside fortran". You just never find out. That is exactly the same as your sleep example, just there you didn't make the loop infinite. |
As to why using I think Python could solve it with some mechanism, and maybe there are some smart ways, but you can look at |
Ah that makes sense @seberg. I was actually thinking of running the C extension code in a |
Well, if the subroutine main
integer :: i
call register_kill_signal_handler()
do i=1,100000
print*, "zzz..."
end do
end subroutine main With the handler defined as: void register_kill_signal_handler_() {
if (signal(SIGINT, SIG_DFL) == SIG_ERR) {
exit(EXIT_FAILURE);
} This terminates in a more friendly manner (though it doesn't return to the Python interpreter). |
I guess the core problem of this issue is that as it stands long running or buggy Also I was initially eyeing |
Well, using So, if you do hold the GIL, even signal handler replacement may be OK (but maybe using the checking API is fine). Maybe that is fine for f2py even?! |
Yes, but what happens if you replace the handler twice from different places? |
I was thinking that with this, the Fortran call itself will reset the handler because of |
Ah I think I get it, so the concern is if: Fortran code sets handler, executes For this I'm not sure. |
The problem is not getting the interrupt to work in your code, the problem is only about restoring behaviour once you are done. Now, maybe you can just always restore the default Python behaviour (and assume that is always right), no matter what the state was when you replaced it with your handler. In a multi-threaded context that may mean we go back to the default while threads are still running: OK.
Yes, basiclaly:
user presses |
I thought I added a scary comment to |
Perhaps it would be simplest if the
user presses ctrl+c AFTER everything is done:-> Still gets the python handler |
The way I'm looking at this is as long as it is impossible for two |
Doesn't the handler set a But yeah, if we know the Python handler and reset to it generously, I expect things are OK (so long nobody else does dangerous stuff because they use I do think there may be solutions to this, i.e. keep a linked list of all handlers around and each context can remove itself from there in a thread-safe way (the magic of linked lists :)). Or maybe some Python context-var... It just feels that without a process-wide unified ABI it is all pretty doomed to have traps in a threaded environment (but, if you wait long enough that you want a snappy ctrl+c, you probably also want to release the GIL). |
nit: "f2py does not respect SIGINT" does not make much sense. f2py just creates Python C/API wrapper functions to Fortran/C functions. Exactly the same structure would be used when writing these wrapper functions manually. f2py has nothing to do with how Python or Fortran/C functions behave with respect to handling signals. Recall: f2py is not a compiler. |
I agree in general, but in this case the issue is that the wrapper generated by Though I think it is perhaps less a bug and more an ENH? |
f2py
does not respect SIGINT (CTRL-C)f2py
wrappers should respect SIGINT (CTRL-C)
I don't think it uses a Infact I was surprised by the usage of |
For testing, you can use If the enhancement is implemented, use the same approach as for |
I agree with Pearu, I don't really understand. Maybe f2py wrappers can check if an interrupt has occurred while Fortran was running, but it doesn't really seem to matter, since Python checks all the time anyway? Rather, you want to add API to allow authors to write interruptable Fortran code. And we don't even have API for that in the NumPy C-API?! This seems all pretty running around in circles, you don't really want to register, this one:
right? It just kills the whole program! Look at what I suppose there may be ways to do that in a thread-safe manner, but I don't know. But we will probably only run in circles until we are clear what you even want here. Maybe, we can store the long-jump points in a linked-list or thread-local, so that "our" (i.e. the NumPy handler) will clean up correctly and only if it is restoring something sensible there... But I honestly don't know and presumably thread-local doesn't work because the signal handler runs in a main thread (or its own thread on windows) or so. |
I was thinking this might be a reasonable default: static void sigint_handler(int signo){
PyErr_SetInterrupt(); // Tells the Python interpreter that SIGINT occured, works with/without the GIL
}
void register_kill_signal_handler_() {
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
exit(EXIT_FAILURE);
}
} Which essentially lets the regular Python handler deal with it. The problem right now is that even if Python is checking all the time it is being ignored, e.g. the original infinite loop or even the non-infinite version: subroutine main
integer :: i
call register_kill_signal_handler()
do i=1,100000
print*, "zzz..."
end do
end subroutine main As it stands, currently this runs till the loop finishes, ignoring any signals, which seems to be unintended behavior, and which is what should return to the interpreter. I'm not sure why we need a
Which I interpreted to mean that |
Is the underlying issue summarized by "This now ignores all CTRL-C and other signals."? If this is the case and the aim is to stop long-running Fortran jobs, then one can use the following workaround (requires bash):
|
Yup, and equally sending |
Here follows a working example of catching SIGINT from f2py generated wrappers. Consider the following Fortran code: ! File: signal_example.f90
subroutine main
integer :: i
do i=1,100000000
print*, "zzz...", i
end do
end subroutine main Create a signature file using: f2py -m signal_example -h signal_example.pyf signal_example.f90 Now edit python module signal_example ! in
interface ! in :signal_example
subroutine main ! in :signal_example:signal_example.f90
usercode '''
jmp_buf jump_buffer;
int val = setjmp( jump_buffer );
if (val == SIGINT) {
PyErr_SetString(PyExc_KeyboardInterrupt, "interrupted main");
return NULL;
}
void sigint_handler(int signo){
longjmp(jump_buffer, signo);
}
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
return NULL;
}
'''
end subroutine main
end interface
end python module signal_example Build the extension module using: f2py signal_example.pyf signal_example.f90 -c Consider the following Python script: import signal_example
try:
signal_example.main()
except KeyboardInterrupt as msg:
print("Received:", msg)
print('Exiting Python') Run it and hit CTRL-C at some point. A result is:
|
Yes, but that code doesn't offer a solution for the FFT problem! It is basically a worse version of I expect we can formulate solutions to the FFT problem under the assumption that no one else does the dangerous stuff that Maybe what I should be saying is: I think this needs more serious grounds-up effort and understanding if you really want to fix it. For FFT luckily, it just didn't really matter anymore because of bluestein's algorithm (it never really runs forever, so ctrl+c isn't really needed). Right now, the only tool that is obviously safe is running the following once in a while and aborting if it returns 1:
|
@seberg, the just-cooked-up example above should be taken as it is. There is no guarantee that it will work under a multithreaded setup cleanly. I think the scope of this issue is a little scattered. For instance, I am not familiar with the FFT problem which is not mentioned in the description, and I think the resolution of this issue may depend on an application as discussed below. First, the expected behavior of using CTRL-C may have various correctness levels:
Another part of the issue is about a realistic resolution for a particular use case. For instance, we can have the following use cases:
These use cases illustrate that the behavior of Ctrl-C will largely depend on the user of f2py: we can only provide tools for users to shoot or not shoot their left legs.. From the f2py perspective, I think this issue should not have a resolution that is applied by default to all generated wrapper functions because Ctrl-C usage is just irrelevant for many cases. This is similar to However, for the correctness level 1, I see a practical use from introducing a new f2py statement, say, |
@pearu, the FFT problem is the problem of multiple threads setting up a sigint handler but not being able to restore the correct behaviour cleanly. It was very easy to observe in old NumPy versions, because even
My whole argument is that I don't think this issue is even actionable. At least not without spending some more thought on solving the issue of restoring the correct signal handler in a multi-threaded environment (or finding another solution). Unless you have a use-case where you prefer risking crashing the interpreter (even after your task is complete) to make |
But, yes, if f2py does not use |
I agree. It sounds like a nicely complicated problem that could be fun to tackle :) |
Describe the issue:
Consider the following Fortran subroutine with an infinite loop. Please note that no one in their right mind would write this, but it illustrates the point.
Now this can be compiled into a nice extension module
f2py -m sloop -c bla.f90
as can be seen in the reproducing code example.Handling custom signals set via Python's
signal
library is out of scope for this issue.Reproduce the code example:
Error message:
This now ignores all
CTRL-C
and other signals. Note that as implemented, callback functions do handle signals in a sense (i.e. they are mostlypython
which does work with signals and avoid executingfortran
when a signal is caught).NumPy/Python version information:
The text was updated successfully, but these errors were encountered: