-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fixed bug #77335 (SA_RESTART despite SIGALRM unless no restart) #3717
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
Conversation
Is it possible to write a test case for this change ? |
@krakjoe not at this moment, but I suspect it's similar to the problem reported on this page by wally at soggysoftware dot co dot uk. Let me see what I can do |
I think this may introduce some unexpected behaviour for some people. I ran into atleast one instance where people are expecting pcntl_signal() to work as if the 3rd argument (restart_syscalls) is With this patch, For the patch to land I think the default for restart_syscalls should become |
Hi @krakjoe @mathieuk and I have some brief discussion regarding this patch (see my response and his subsequent comment for msphpsql issue 885) While I agree that my current proposed fix may introduce other issues, our recent findings did prove that the PCTNL way of calling signal handlers is flawed. I am inclined to fix the PCTNL code rather than changing the existing API however. |
So to summarize:
I think this would be the ideal change to make.
So that last one might be tricky. It introduces the concept of calling non-signal-safe functions. We might cover that with documentation. But if that's deemed too risky, maybe we could add the ability to register signal handlers that you want called directly? Like:
Which makes it a power-user kinda thing, which I think a lot of PCNTL-using code already is. |
@mathieuk Running PHP code inside a signal handler is definitely a no-go. We used to allow such things and it kinda somewhat works -- until the stars align and you have a malloc deadlock. PHP does not provide a sufficiently controlled environment to execute code in signal handlers. |
@@ -43,7 +43,7 @@ Sigfunc *php_signal4(int signo, Sigfunc *func, int restart, int mask_all) | |||
#ifdef HAVE_STRUCT_SIGINFO_T | |||
act.sa_flags |= SA_SIGINFO; | |||
#endif | |||
if (signo == SIGALRM || (! restart)) { | |||
if (signo == SIGALRM && (! restart)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than changing this to an &&
, shouldn't the signo == SIGALRM
part just be dropped? We still want to allow using SA_RESTART with non-SIGALRM signals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please see my original intention but it might be drastic and break other existing code.
Yeah, makes sense. Any ideas on how the userland signal handler could still be called (timely) with SA_RESTART enabled? |
@mathieuk Not really. I think we should do the change to allow setting SA_RESTART for SIGALRM, but it's going to be an either/or thing. Either you get a reliable interrupt, or you get restarting syscalls, but you won't be getting both... We should only land this for master though. As you mentioned, this may break user code that is relying on the current (undocumented) exception for SIGALRM. |
@nikic is there any path forward that would allow this to not be a bug in the 7.3 release cycle? |
@ralphschindler I think we could do the following:
That is, if no explicit value is passed, then we will stick with the (effective) current default of SA_RESTART for everything but SIGALRM, but have the option of explicitly passing Not sure how much this will really solve in the end -- if you're using SIGALRM quite likely you do want to interrupt syscalls, at which point you're back where you started. |
@nikic @mathieuk @ralphschindler
As indicated here "Some older systems, such as SunOS, define the SA_INTERRUPT flag. These systems restart interrupted system calls by default, so specifying this flag causes system calls to be interrupted. Linux defines the SA_INTERRUPT flag for compatibility with applications that use it, but the default is to not restart system calls when the signal handler is installed with sigaction. The XSI extension of the Single UNIX Specification specifies that the sigaction function not restart interrupted system calls unless the SA_RESTART flag is specified." In short, Linux behaves completely the opposite way from the older systems like SunOS. This makes interrupting system calls questionable, |
@nikic @mathieuk @ralphschindler Upon more investigation, in many places it says that "SA_INTERRUPT is a no-op left for historical reasons or backward compatibility. That is, it is unused or not really recommended. You can also look for SA_INTERRUPT on signal.h and check its comment. Nonetheless, below is a very simple test case with different combinations (borrowed from this example and inspired by the latest script provided by @mathieuk). Only one task is able to write to the file. Hopefully the test scenarios help moving things forward.
Tested my original patch in this PR as well as this one (dropped the first condition) -- seemingly no difference.
The output of running
The output of running
The output of running
|
@nikic @yitam The main issue - for me, and I think others in the same boat (relying on SIGALRM to protect against runaway/stalled jobs in a worker daemon) - is that the MSSQL driver would cause an exception to be thrown before the signal handler was being called. That mucks with my ability to deal with problems. With my current understanding that happens because:
While providing proper SA_RESTART control will prevent ODBC from causing an exception to be thrown it might be useless to me as I can't be sure my signal handler will ever be called. One may question what should happen first here: should the signal be processed first (an 'interrupt' was declared after all) or should the exception be thrown as-is? If I could get the signal handler first, I might be able to do my housekeeping (update some job status somewhere) and kill the process, so the exception wouldn't matter anymore (Laravel's queue worker basically works that way). @nikic I understand this is grasping at straws but would there even be a way to do that? If not, it seems my options are a) creating a worker that forks each job and handles error situations from the child, b) disable exceptions for PDO and handle error values or c) hope the ODBC driver starts handling EINTR ánd is extended to have client side timeouts (otherwise handling EINTR would leave me in the same place as enabling SA_RESTART on the signal handler). |
@mathieuk The real problem in the original pcntl code is that it explicitly disables SA_RESTART for SIGALRM, which is error prone as already explained by the pgsql patch. In short, one can not (or should not) assume all system processes can be fully EINTR-proof, not just ODBC. Now I'm inclined to propose the following change instead, which makes sense if the users specifically set restart to
Yes @mathieuk that sounds like a good workaround. You might want to read about potential race conditions discussed above the example on this page, in which it says, "A common use for alarm, in addition to implementing the sleep function, is to put an upper time limit on operations that can block." |
I've opened #3742 to implement the variant I described in #3717 (comment). I think this is the best we can do here. If you need both support for a reliable alarm and want to use libraries that are not interrupt safe, then I think the only option is to run the alarm in a separate process. |
Sounds good @nikic |
Closing this in favor of #3742, which is now merged. |
For details please refer to bug report 77335