Skip to content
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

PDOException thrown whenever SIGALARM occurs during a query #885

Closed
mathieuk opened this issue Nov 20, 2018 · 32 comments
Closed

PDOException thrown whenever SIGALARM occurs during a query #885

mathieuk opened this issue Nov 20, 2018 · 32 comments

Comments

@mathieuk
Copy link

mathieuk commented Nov 20, 2018

+## PHP Driver version or file name

+## SQL Server version
SQL2016

+## Client operating system
CentOS 7

+## PHP version
PHP 7.2.11

+## Microsoft ODBC Driver version
5.3.0

+## Problem description
When you use PCNTL to protect execution time of a worker like with Laravel's Job Workers and a signal arrives (for instance SIGALRM) queries currently running will throw PDOExceptions:

PHP Fatal error:  Uncaught PDOException: SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]TCP Provider: Error code 0x2714 in test2.php:14

This prevents the alarm handler from actually being called.

+## Expected behavior and actual behavior
I think I understand why this throws an exception. The whole call is ending up in an inconsistent state as you don't know what will happen in the signal handler function. It doesn't make sense to think the current query will successfully finish even if the signal handler doesn't do anything bad and yields control quickly enough.

I'm not entirely sure what should happen besides that I would expect the signal handler to be called; perhaps before the exception is thrown.

+## Repro code or steps to reproduce

Minimized example, obviously not real life code :)

$pdo = new PDO('sqlsrv:Server=;Database=', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

pcntl_async_signals(true);
pcntl_signal( SIGALRM, function () { echo "I should shutdown"; });
pcntl_alarm(2);

while(1) {
        $testresult = $pdo->query("SELECT * FROM users;");
        foreach($testresult as $r) {}
}
@david-puglielli
Copy link
Contributor

@mathieuk Thank you for this info - we will test out the repro you provided and get backto you shortly.

@david-puglielli
Copy link
Contributor

Ok we can reproduce this behaviour. Do you see something similar happening with sqlsrv?

@mathieuk
Copy link
Author

It yields similar, yet subtly different, behaviour:

<?php

$serverName = '';
$connectionOptions = [];

//Establishes the connection
$conn = sqlsrv_connect($serverName, $connectionOptions);

pcntl_async_signals(true);
pcntl_signal( SIGALRM, function () { echo "I should shutdown"; });
pcntl_alarm(2);

$errors = 0;

while(1) {
        echo "=> Starting query\n";
        $testresult = sqlsrv_query($conn,"SELECT * FROM users;");
        echo "\t - Query done\n";

        if (!$testresult) {
                $errorMsgs = sqlsrv_errors(SQLSRV_ERR_ALL);

                foreach ($errorMsgs as $error) {
                        echo "\n", $error['message'], "\n";
                }

                if ($errors++ > 5) exit;

                continue;
        }

        echo "\t - Fetching data\n";
        $rowCount = 0;

        while ($r = sqlsrv_fetch_array($testresult)) { /* echo "\t - Got row #$rowCount\n"; */ $rowCount++; }

        echo "<= Query complete\n";
}

Results in a few variations of:

/* ... X succesfull calls snipped for brevity ... */ 

=> Starting query
	 - Query done
	 - Fetching data
<= Query complete
=> Starting query
I should shutdown	 - Query done

[Microsoft][ODBC Driver 17 for SQL Server]TCP Provider: Error code 0x2714

[Microsoft][ODBC Driver 17 for SQL Server]Communication link failure
=> Starting query
	 - Query done

[Microsoft][ODBC Driver 17 for SQL Server]The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.
=> Starting query
	 - Query done

[Microsoft][ODBC Driver 17 for SQL Server]The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.
=> Starting query
	 - Query done

[Microsoft][ODBC Driver 17 for SQL Server]The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.
=> Starting query
	 - Query done

A few remarks/observations:

  • This only seems to occur whenever SIGALARM arrives when we're in the query execution. Haven't seen anything (yet) indicating similar behaviour during data fetching.

  • With SQLSRV the signal handler seems to always be called. I haven't seen errors happening before the signal handler was called and I always got the output specified in the signal handler. I've tried > 20 times and I did see that happening in the same amount of tries with PDO. SQLSRV doesn't throw exceptions so the behaviour I'm seeing with PDO may well be caused by the way pcntl + exceptions in PHP work. Perhaps the extension can yield to the signal handler when it's interrupted, before throwing the exception? This may be of interest: support VM_RETURN vm_interrupt php/php-src#2902

  • The initial error messages aren't always the same. Sometimes it's Error code 0x2714; other times it's SMux Provider: Physical connection is not usable [xFFFFFFFF].

  • I understand the query not completing successfully but I didn't expect the connection to fail completely here.

  • Just to be complete, there are instances where the signal arrives and is handled succesfully. Presumably we're not doing any queries at that time :)

HTH

@yitam
Copy link
Contributor

yitam commented Dec 5, 2018

@mathieuk please check your unixodbc version odbcinst -j. I can't seem to reproduce this with unixodbc 2.3.6. I also tested with Ubuntu 18.04 (unixodbc 2.3.4). No problem either.

@yitam
Copy link
Contributor

yitam commented Dec 5, 2018

@mathieuk never mind. I could reproduce this by re-running the same php script in a different terminal. Will start investigating.

@yitam
Copy link
Contributor

yitam commented Dec 5, 2018

@mathieuk looks like whenever SIGALRM arrives, the connection is broken, as indicated by SQLSTATE 08S01 as well as all the error messages you have listed in the sqlsrv example.

When it fails to connect, the query fails to execute, so an exception is thrown. As you said, there is no exception after the signal handler is done and yields control. This is apparent when we keep trying to run the same php script again and again.

Maybe it will help us understand the problem if you can provide more details?

@mathieuk
Copy link
Author

mathieuk commented Dec 6, 2018

The first thing is about the signal handler. When SIGALRM comes in the connection is often immediately broken and an exception is thrown before the alarm handler can be called. Even if the exception is handled, the alarm handler is never called. This is confusing for the surrounding code (yay semi-concurrency).

Things would be clearer if the signal handler would still be called; before the exception.

Then the second thing is that the connection is completely terminated from the second SIGALRM comes in. That strikes me as odd; it seems to me that yes, the query may be interrupted and that might be unrecoverable for that query but it seems odd that the connection is completely broken at that point. Would it be possible to recover and issue new queries on the connection?

@yitam
Copy link
Contributor

yitam commented Dec 7, 2018

I see your points @mathieuk
We will investigate the causes of broken connections and get back to you on this.

@ralphschindler
Copy link

ralphschindler commented Dec 11, 2018

@david-puglielli For what it's worth, I have experienced the same symptoms (random exceptions of 0x2714) when using Laravel's Horizon (Job Worker) stack. I cannot confirm it is specifically SIGALRM (or any other signal), but something is definitely at odds with the PDO SQLSrv connection inside a Laravel Worker.

If I run the same code outside a worker, it works as expected and without any TCP errors. This is running with 5.5.0-preview+11606, unixodbc/xenial,now 2.3.1-4.1, php 7.2.

update: tagged repo dev instead :)

@yitam
Copy link
Contributor

yitam commented Dec 12, 2018

@mathieuk and @ralphschindler

After some investigation, disabling MARS seems to have reduced the likelihood of broken connections. To disable MARS, which is true by default, you need to set it to false, as shown below:

$pdo = new PDO('sqlsrv:Server=yourServer; Database=yourDB; MultipleActiveResultSets=false', 'yourUser', 'yourPassword');

@mathieuk: we are still testing (with the latest ODBC drivers, post CTP preview), and please shed some light on how to reproduce this issue. If I see "I should shutdown" when running in a command terminal, I leave it running and launch another terminal to run the same php script. Yet @david-puglielli did it by canceling (CTRL+C) and re-ran the script in the same terminal window.

@yitam yitam added the odbc label Dec 12, 2018
@ralphschindler
Copy link

@yitam unfortunately, adding that setting does not reduce the likelihood of receiving SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]TCP Provider: Error code 0x2714. if you have any further fixes, I am more than willing to try. Thanks!

@yitam
Copy link
Contributor

yitam commented Dec 12, 2018

@ralphschindler when I tested with ODBC driver 17.2 the problem frequently occurred indeed but with an internal build (post CTP preview) it does seem to have improved. In the meantime, your patience is appreciated while odbc team is looking into this.

@mathieuk
Copy link
Author

mathieuk commented Dec 13, 2018

@yitam

I've updated my repo script to hopefully make things a bit clearer. It's attached below (test-pdo.php). When you run it it will eventually run into the error situation, every time you run the script.

The repro script simulates a typical worker daemon like you'd see in Laravel.

test-pdo.php

<?php

$pdo = new PDO('sqlsrv:Server=;Database=', '', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

function handleAlarm() {
        echo "Received SIGALRM!";
        pcntl_alarm(2);
}

pcntl_async_signals(true);
pcntl_signal( SIGALRM, 'handleAlarm');
pcntl_alarm(2);

while(1) {
        $testresult = $pdo->query("SELECT * FROM users;");
        foreach($testresult as $r) {}
        echo '.';
}

FWIW, it differs in that a worker in Laravel would set the alarm at the beginning of each loop instead of whenever the alarm went off. That way the last alarm would be replaced with a new one so it doesnt go off unnecessarily. In my case, I -want- it to go off unnecessarily as I want to see the effect on queries.

When I run this I see two different outcomes:

$ php test-pdo.php
..............................................................
..............................................................
..............................................................
..............................................................
..................Received SIGALRM!.............
...............................................................
...............................................................
...............................................................
...............................................................
Received SIGALRM!...............................
...............................................................
...............................................................
...............................................................
......................................PHP Fatal error:  
Uncaught PDOException: SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]TCP Provider: Error code 0x2714 in test-pdo.php:16
Stack trace:
#0 test-pdo.php(16): PDO->query('SELECT * FROM u...')
#1 {main}
  thrown in test-pdo.php on line 16

In this case the alarm was successfully handled 2 times (read: we were not executing the query at that time) and then the third time it was executing the query and the exception was thrown. Note how the message "Received SIGALRM!" is absent that time.

This is the worst case as now I wasn't able to clean-up after something went wrong. This can result in 'run-away' workers in the case of Laravel workers. In my case it also messes up some job housekeeping that I have to do.

If I run the script a few times I'll also run into this variant:

$ php test-pdo.php
................................................................................
................................................................................
................................................................................
.........................Received SIGALRM!PHP Fatal error:  
Uncaught PDOException: SQLSTATE[08S01]: 
[Microsoft][ODBC Driver 17 for SQL Server]Session Provider: Physical connection is not usable [xFFFFFFFF].  in test-pdo.php:17
Stack trace:
#0 {main}

Next PDOException: SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]Session Provider: Physical connection is not usable [xFFFFFFFF].  in test-pdo.php:17
Stack trace:
#0 {main}

Next PDOException: SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]Session Provider: Physical connection is not usable [xFFFFFFFF].  in test-pdo.php:17
Stack trace:
#0 {main}

Next PDOException: SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]Session Provider: Physical connection is not usable [xFFFFFFFF].  in test-pdo.php:17
Stack trace:
#0 {main}

Next PDOException: SQLSTATE[08S01]: [Microsoft][ODBC Driver 17 for SQL Server]Session Provider: Physical connection is not usable [xFFFFFFFF].  in test-pdo.php:17
Stack trace:
#0 {main}

Next  in test-pdo.php on line 17

In this case the signal handler WAS called but then the connection was completely broken.

Does this help?

(edited the output a bit to make it clearer in the github issue)

@mathieuk
Copy link
Author

Like @ralphschindler, I did not see any difference in behaviour when using MultipleActiveResultSets=false.

@yitam
Copy link
Contributor

yitam commented Dec 13, 2018

Thanks @mathieuk for a more detailed repro scenario. It certainly helps with the investigation.

I did not see any difference in behaviour when using MultipleActiveResultSets=false.

I should have mentioned that this makes a small difference but only with the internal ODBC build. Thanks for trying nonetheless.

@yitam
Copy link
Contributor

yitam commented Dec 21, 2018

@mathieuk and @ralphschindler

After some more investigation with ODBC team, we figure the culprit lies with pcntl's omission of SA_RESTART. Essentially, it is preventing SIGALRM from specifying SA_RESTART, which is necessary in order not to interrupt the ODBC driver operation. Specifying SA_RESTART (or using default from signal() which does) in a native ODBC application does not reproduce this bug, even running it overnight.

FYI, please find it attached and this is a similar explanation of the bug

testalarm.zip

I've modified this line (git diff shown below) and been running the pdo_sqlsrv repro script above with the latest stable version ODBC 17.2. There is no need to turn off MARS, and as of now it is still running (more than half an hour already) without any exception. I've also filed a bug report accordingly.

diff --git a/ext/pcntl/php_signal.c b/ext/pcntl/php_signal.c
index 32a6c55c93..161455e002 100644
--- a/ext/pcntl/php_signal.c
+++ b/ext/pcntl/php_signal.c
@@ -41,7 +41,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)) {
 #ifdef SA_INTERRUPT
 		act.sa_flags |= SA_INTERRUPT; /* SunOS */
 #endif

@yitam yitam removed the odbc label Dec 21, 2018
@ralphschindler
Copy link

ralphschindler commented Dec 22, 2018

@yitam do you feel comfortable making a PR to the php project, or shall I to usher this along?

@yitam
Copy link
Contributor

yitam commented Dec 24, 2018

hi @ralphschindler thanks for your reminder. Yes maybe I should make a pull request.

@ralphschindler
Copy link

👍 - let me know if you need any assistance :)

@yitam
Copy link
Contributor

yitam commented Dec 24, 2018

Thanks @ralphschindler ! Done, please check PR 3717

@yitam
Copy link
Contributor

yitam commented Dec 31, 2018

@ralphschindler

  • let me know if you need any assistance :)

Yes, can you think of a test case without using our PDO driver?

@mathieuk
Copy link
Author

mathieuk commented Jan 2, 2019

@yitam I've tried the change shown in php/php-src#3717 and I have the basis for a test that you could use.

For this comment I'm using http://man7.org/linux/man-pages/man7/signal.7.html as a reference.

So I think what you're saying is that SIGALRM is triggering a EINTR return value for a read() or write() (or somesuch) call in the ODBC driver which causes it to return an error which in turn causes PDO to throw an exception. By enabling SA_RESTART that read() call would be restarted right after the signal handler returns and no bad things happen.

It seemed to me that flock() was one of the easier 'primitives' to test this without this extension. What I'm seeing is that indeed: without your change the behaviour is always as if argument 3 'restart_syscalls' to pcntl_signal() is false. This means that the function call is interrupted, my signal handler is called, and then the function call returns with an error. In the case of flock() that's just a returned false value.

With your change, the function call is interrupted but it appears my signal handler is never called. This means that, while there were no exceptions from PDO_SQLSRV, other functionality may now be broken. If the restarted read() in the ODBC driver never returns (because of a hung connection?) my process will hang even though I thought to be protected against that by using pcntl_alarm.

That behaviour seems to come from pcntl actually queuing signal handler functions to be called by pcntl_signal_dispatch which might be called by the pcntl_interrupt_function (which overrides the global zend_interrupt_function), the tick function and/or the user-land version of pcntl_signal_dispatch.

So, with pcntl_async_signals(true) it'd wait for control to be handed back to the Zend VM, which would call the zend_interrupt_function function pointer (to pcntl_interrupt_function) which would call pcntl_signal_dispatch which calls the user land signal handler. Or, it might wait for the next tick which would call pcntl_signal_dispatch which calls the user land signal handler. Or it might wait for the next manual call to pcntl_signal_dispatch.

None of these things are happening with SA_RESTART enabled because as soon as the signal handler returns (that is: when ext/pcntl/pcntl.c pcntl_signal_handler returns) the syscall that was interrupted is restarted automatically. That is: before PHP has had the chance to yield to the Zend VM for the interrupt to be handled, or the next tick to be registered or a manual call to be performed, so that it could call the actual signal handler. It then just hangs in flock() forever.

So now we have two issues. One might say the PCTNL way of calling signal handlers is flawed as they get called outside of the actual signal handler rendering SA_RESTART pretty useless? OTOH one might say the ODBC driver should just handle EINTR properly (retrying for EINTR instead of erroring out).

What do you think ?

Here is the flock-based test script I used:

<?php

/**
 * In tab 1, run: `php test.php`. This will lock the file 'lock.txt' with LOCK_
EX.
 * In tab 2:, run `php test.php alarm`. This will try to set the same lock and
gets caught by the alarm. Or not.
 *
 * For difference in behaviour, set the 3rd arg to pcntl_signal to true/false a
nd or apply the patch by @yitam https://github.com/php/php-src/pull/3717.
 */

$wantAlarm = ($_SERVER['argv'][1] ?? '') === 'alarm';

if ($wantAlarm) {
        echo "Yes, alarm please\n";
        pcntl_async_signals(true);
        pcntl_signal( SIGALRM, function () { pcntl_alarm(1); }, true);
        pcntl_alarm(3);
}

$fp = fopen('lock.txt', 'r');
echo "Locking";

$result = flock($fp, LOCK_EX);
var_dump($result);

if (!$wantAlarm) { while(true) { sleep(1); } }

@yitam
Copy link
Contributor

yitam commented Jan 2, 2019

Thanks @mathieuk for your detailed test scenario and script. I haven't had time to try it out yet, but as you can see in my earlier reply, there wasn't any issue using signal / alarm in the native ODBC program.

What I'm seeing is that indeed: without your change the behaviour is always as if argument 3 'restart_syscalls' to pcntl_signal() is false.

Yes that is exactly the case. My patch was merely an attempt to show that PCTNL way of calling signal handlers is flawed. The third argument does not make any difference with SIGALRM. For this reason we quoted the pgsql fix to justify why we should consider the following:

Use SA_RESTART for all signals, including SIGALRM, in which it said (which you might have already noticed):

Nowadays, allowing it to interrupt kernel calls doesn't seem like a very good idea, since its 
use for statement_timeout means SIGALRM could occur anyplace in the code, and there are 
far too many call sites where we aren't prepared to deal with EINTR failures.  

According to the above, I was tempted to make a drastic change like this (which I haven't tested):

-	if (signo == SIGALRM || (! restart)) {
+	if (! restart) {
 #ifdef SA_INTERRUPT
 		act.sa_flags |= SA_INTERRUPT; /* SunOS */
 #endif

That being said, to make the 3rd argument 'restart_syscalls' to pcntl_signal() false by default, that is, changing the API, might break others' existing scripts.

As soon as I have time I will try your test case and/or continue some more testing.

@mathieuk
Copy link
Author

mathieuk commented Jan 3, 2019

I also tried with async_signals=false and using declare(ticks=1) to see if using the tick function made any difference, but it doesn't. This is basically happening practically 'outside' of PHP's control.

The bigger fix indeed seems more appropriate but I totally understand your hesitation. I think it should be less of an issue when the argument restart_syscalls to pcntl_signal() is changed to be default false. That seems like an API breaking change, but it is in fact keeping the expected behaviour for existing scripts (that are not providing restart_syscalls=true) in tact.

That leaves us with PCNTL potentially not being able to (timely) call the userland signal handler if the primitive used is taking a long time or just blocks forever when SA_RESTART is enabled. I'm not sure what options there are for changing/improving that.

I think we should either update your bug report to include the above; or maybe report a new one regarding the things I described above?

@yitam
Copy link
Contributor

yitam commented Jan 4, 2019

hi @mathieuk

Here is the flock-based test script I used:

I checked your script and not exactly sure what you're trying to do:

  1. without the alarm, it's just an infinite while loop?
  2. with the alarm, it locks the text file and exits right away.

I modified your script slightly as follows,

if ($wantAlarm) {
        echo "Yes, alarm please\n";
        pcntl_async_signals(true);
        pcntl_signal( SIGALRM, function () { echo "ALARM! "; pcntl_alarm(1); });
        pcntl_alarm(3);
}

$fp = fopen('lock.txt', 'r');
echo "Locking" . PHP_EOL;

$result = flock($fp, LOCK_EX);
var_dump($result);

while(true) { sleep(1); }

And I got the following output:

Yes, alarm please
Locking
bool(true)
ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ALARM! ^C

Makes no difference whether I made the new change or not:

        act.sa_flags = 0;
#ifdef HAVE_STRUCT_SIGINFO_T
        act.sa_flags |= SA_SIGINFO;
#endif
        if (! restart) {
        // if (signo == SIGALRM && (! restart)) {
#ifdef SA_INTERRUPT
                act.sa_flags |= SA_INTERRUPT; /* SunOS */
#endif
        } else {

@mathieuk
Copy link
Author

mathieuk commented Jan 5, 2019

@yitam you had to run 2 instances (one with 'alarm' as argument) at the same time for that to work. I've changed it into a new, single, script that tests 3 scenarios. It builds on the fact that the signal handler actually doesn't get called with SA_RESTART and flock(). You can find it here: https://gist.github.com/mathieuk/9495b76da54ead09297deb62915d4f46

Output is as follows:

// PHP without @yitam's patch:
$ php fork.php
Runnning 'Without SA_RESTART, without alarm, interrupted flock fails' .... SUCCESS
Runnning 'Without SA_RESTART, with alarm, signal handler gets called' .... SUCCESS
Runnning 'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' .... FAILED! Got exitcode 3 (EXITCODE_ALARM_HANDLED) instead of 0 (EXITCODE_PARENT_INTERVENED)

// PHP with @yitam's patch:
$ ~/php-7.2.13/sapi/cli/php fork.php
Runnning 'Without SA_RESTART, without alarm, interrupted flock fails' .... SUCCESS
Runnning 'Without SA_RESTART, with alarm, signal handler gets called' .... SUCCESS
Runnning 'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' .... SUCCESS

Atleast it tests expected behaviour.

@mathieuk
Copy link
Author

mathieuk commented Jan 5, 2019

@yitam judging by the comments of Nikic Popov it appears that PCNTL won't be able to work in such a way that SA_RESTART will be actually useful in my initial stated problem. With the SA_RESTART flag I cannot trust on my signal handler being called. In that situation not using SA_RESTART would actually be better as I can atleast catch the exception, reconnect and do my housekeeping then. It's not pretty but it works.

It seems that while PGSQL (like you referenced) uses SA_RESTART MySQL actually fully implemented EINTR. Would it be possible to post handling EINTR as a feature request with the ODBC team?

@yitam
Copy link
Contributor

yitam commented Jan 7, 2019

Thanks @mathieuk your latest script makes sense. However, when running a quick test without my patch (reverted to the original php way of handling alarm signal), my output is different from yours:

Runnning 'Without SA_RESTART, without alarm, interrupted flock fails' .... FAILED! Got exitcode 0 (EXITCODE_PARENT_INTERVENED) instead of 2 (EXITCODE_LOCK_INTERRUPTED)
Runnning 'Without SA_RESTART, with alarm, signal handler gets called' .... SUCCESS
Runnning 'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' .... FAILED! Got exitcode 3 (EXITCODE_ALARM_HANDLED) instead of 0 (EXITCODE_PARENT_INTERVENED)

@mathieuk
Copy link
Author

mathieuk commented Jan 7, 2019

Can't seem to reproduce @yitam. What distro/versions are you using?
Can you confirm it's not getting into the if ($locked === false) { } if-statement?

// Clean build of PHP 7.3.0 (./configure --enable-pcntl)
$ ~/php-7.3.0/sapi/cli/php form2.php
Runnning 'Without SA_RESTART, without alarm, interrupted flock fails' .... SUCCESS
Runnning 'Without SA_RESTART, with alarm, signal handler gets called' .... SUCCESS
Runnning 'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' .... FAILED! Got exitcode 3 (EXITCODE_ALARM_HANDLED) instead of 0 (EXITCODE_PARENT_INTERVENED)

// PHP 7.2 built with @yitam's patch (./configure --enable-pcntl)
$ ~/php-7.2.13/sapi/cli/php form2.php
Runnning 'Without SA_RESTART, without alarm, interrupted flock fails' .... SUCCESS
Runnning 'Without SA_RESTART, with alarm, signal handler gets called' .... SUCCESS
Runnning 'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' .... SUCCESS

// PHP 7.2 from Remi's repo
$ php form2.php
Runnning 'Without SA_RESTART, without alarm, interrupted flock fails' .... SUCCESS
Runnning 'Without SA_RESTART, with alarm, signal handler gets called' .... SUCCESS
Runnning 'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' .... FAILED! Got exitcode 3 (EXITCODE_ALARM_HANDLED) instead of 0 (EXITCODE_PARENT_INTERVENED)

@yitam
Copy link
Contributor

yitam commented Jan 7, 2019

I think my env was a bit messed up @mathieuk
Now I managed to see what you showed above. Will investigate more.

@yitam
Copy link
Contributor

yitam commented Jan 14, 2019

@mathieuk we will leave this issue open and continue monitoring PHP issue 3742 and bug 77335

@yitam
Copy link
Contributor

yitam commented Oct 16, 2019

Tested the fix (see PHP issue 3742 for the detailed discussions) in PHP 7.4.0RC4 today.

No more exception thrown from ODBC driver if the optional argument $restart_syscalls is set to TRUE. That is, change

pcntl_signal(SIGALRM, 'handleAlarm');

to

pcntl_signal(SIGALRM, 'handleAlarm', true);

Thus, I'm closing this issue now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants