msphpsql: PDOException thrown whenever SIGALARM occurs during a query

+## 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) {}
}

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 32 (19 by maintainers)

Most upvoted comments

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.

Thanks @ralphschindler ! Done, please check PR 3717

@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

@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.