Last active
January 5, 2019 11:54
-
-
Save mathieuk/9495b76da54ead09297deb62915d4f46 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// This exitcode means the alarm handler of the parent process killed the child | |
define ('EXITCODE_PARENT_INTERVENED', 0); // MUST be 0 | |
// This exitcode means the child achieved the second lock. That'd be weird. | |
define ('EXITCODE_LOCK_ACHIEVED', 1); | |
// This exitcode means the second lock got interrupted (and returned false). | |
define ('EXITCODE_LOCK_INTERRUPTED', 2); | |
// This exitcode means the alarm handler in the child got handled. We probably don't expect that to happen in the fixed case. | |
define ('EXITCODE_ALARM_HANDLED', 3); | |
function handleHungChildprocess($pid) { | |
return function() use ($pid) { | |
posix_kill($pid, SIGSTOP); | |
//echo "[parent] Killing pid $pid\n"; | |
}; | |
} | |
function runPcntlTest(bool $restartSyscalls, bool $wantAlarm) { | |
$pid = pcntl_fork(); | |
if (!$pid) { | |
// child process that will run the test | |
pcntl_signal( | |
SIGALRM, | |
function() use ($wantAlarm) { | |
if ($wantAlarm) | |
exit(EXITCODE_ALARM_HANDLED); | |
}, | |
$restartSyscalls | |
); | |
pcntl_alarm(1); | |
//echo "[child] I am child #", getmypid(), "\n"; | |
touch('./lock.txt'); | |
$fp = fopen('lock.txt', 'r'); | |
flock($fp, LOCK_EX); | |
$fp2 = fopen('lock.txt', 'r'); | |
$locked = flock($fp2, LOCK_EX); | |
if ($locked === false) { | |
exit(EXITCODE_LOCK_INTERRUPTED); | |
} | |
// we'll never actually get here | |
exit(EXITCODE_LOCK_ACHIEVED); | |
} else { | |
//echo "[parent] running as #", getmypid(), "\n"; | |
// parent process; | |
pcntl_signal(SIGALRM, handleHungChildprocess($pid), false); | |
pcntl_alarm(2); | |
pcntl_waitpid($pid, $status, WUNTRACED ); | |
$exitCode = pcntl_wexitstatus($status); | |
//echo "[parent] Child returned code $exitCode\n"; | |
return $exitCode; | |
} | |
throw new Exception("Couldnt fork"); | |
} | |
pcntl_async_signals(true); | |
$exitCodeMapping = [ | |
EXITCODE_LOCK_INTERRUPTED => 'EXITCODE_LOCK_INTERRUPTED', | |
EXITCODE_ALARM_HANDLED => 'EXITCODE_ALARM_HANDLED', | |
EXITCODE_PARENT_INTERVENED => 'EXITCODE_PARENT_INTERVENED' | |
]; | |
$scenarios = [ | |
'Without SA_RESTART, without alarm, interrupted flock fails' => [ | |
'restartSyscalls' => false, | |
'wantAlarm' => false, | |
'expectedExitCode' => EXITCODE_LOCK_INTERRUPTED | |
], | |
'Without SA_RESTART, with alarm, signal handler gets called' => [ | |
'restartSyscalls' => false, | |
'wantAlarm' => true, | |
'expectedExitCode' => EXITCODE_ALARM_HANDLED | |
], | |
'With SA_RESTART: interrupted flock fails to call signal handler and gets terminated' => [ | |
'restartSyscalls' => true, | |
'wantAlarm' => true, | |
'expectedExitCode' => EXITCODE_PARENT_INTERVENED | |
] | |
]; | |
foreach ($scenarios as $description => $arguments) { | |
echo "Runnning '$description' .... "; | |
$exitCode = runPcntlTest($arguments['restartSyscalls'], $arguments['wantAlarm']); | |
echo $exitCode === $arguments['expectedExitCode'] ? | |
"SUCCESS\n" : | |
sprintf( | |
"FAILED! Got exitcode %d (%s) instead of %d (%s)\n", | |
$exitCode, | |
$exitCodeMapping[$exitCode], | |
$arguments['expectedExitCode'], | |
$exitCodeMapping[$arguments['expectedExitCode']] | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment