PHP script causes the docker container to freeze up. PHP script works fine running on host machine.
260
docker pull maindg/php-multi-process-failure
docker run -it maindg/php-multi-process-failure
FROM microsoft/windowsservercore:1803
# install visual C++ runtime
RUN powershell -Command \
Invoke-WebRequest -Uri "https://aka.ms/vs/15/release/VC_redist.x64.exe" -OutFile "/vc_redist.x64.exe"; \
Start-Process /vc_redist.x64.exe -ArgumentList '/install', '/passive' -NoNewWindow -Wait; \
Remove-Item -Force /vc_redist.x64.exe
# install php
RUN powershell -Command \
# Add as fix for downloading from windows.php.net site due to certificate checks
add-type 'using System.Net;using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } '; \
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'; \
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols; \
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy; \
# End add fix for downloading
mkdir /install; \
Invoke-WebRequest -Uri "https://windows.php.net/downloads/releases/php-7.2.6-nts-Win32-VC15-x64.zip" -OutFile "/install/php.zip"; \
Expand-Archive /install/php.zip -DestinationPath /php; \
Remove-Item /install -Recurse -Force; \
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\php', [EnvironmentVariableTarget]::Machine)
COPY ./GoSlow.php /GoSlow.php
COPY ./TestMultiProcess.php /TestMultiProcess.php
ENTRYPOINT ["php", "/TestMultiProcess.php"]
<?php
declare(strict_types=1);
echo "Starting Sleeping!\n";
sleep($argc > 1 ? (int)$argv[1] : rand(5, 20));
echo "Done Sleeping\n";
<?php
declare(strict_types=1);
$codeCheck = new TestMultiProcess();
$codeCheck->runTest();
class TestMultiProcess
{
/**
* Keeps track of running processes and starting times.
* @var array
*/
public static $processArray;
public function runTest(): void
{
$wholeTime = microtime(true);
$workingDir = __DIR__;
$baseCommand = 'php GoSlow.php';
$processCount = 20;
try {
for ($i = 0; $i < $processCount; $i++) {
$time = time();
$timeLength = rand(1, 8);
echo "Starting {$baseCommand} $i with time: $timeLength\n";
self::$processArray[$i]['startTime'] = $time;
$process = $this->runProcess($baseCommand, [$timeLength], $workingDir);
self::$processArray[$i]['process'] = $process;
}
// Wait for output from each process
while (!empty(self::$processArray)) {
foreach (self::$processArray as $i => $processData) {
/** @var Process $process */
$process = $processData['process'];
if (!$process->isRunning()) {
$process->waitForExit();
echo "\n======================\n";
$startTime = self::$processArray[$i]['startTime'];
if ($process->getStandardErrorResult() !== "") {
throw new RuntimeException($process->getStandardErrorResult());
}
echo "Process $i is Finished\n";
echo $process->getStandardOutResult();
$duration = microtime(true) - $startTime;
echo "\nTime: $duration seconds\n";
echo "\n\n";
unset(self::$processArray[$i]);
}
}
// Wait a quarter second before checking again.
usleep(25000);
}
} catch (Throwable $e) {
echo "\nFailure Starting Process due to Exception: [\n" . $e->getMessage() . "]\n";
exit(2);
}
echo "\nTotal Time: " . (microtime(true) - $wholeTime) . "seconds.";
}
/**
* @param string $process
* @param array $arguments
* @param string $workingDir
* @return Process
*/
private function runProcess(string $process, array $arguments, string $workingDir): Process
{
$process = new Process($process, $arguments);
$process->setCwd($workingDir);
$process->startForOutputCapture();
return $process;
}
}
class Process
{
/**
* @var string
*/
private $command;
/**
* @var array
*/
private $descriptorSpec;
/**
* @var resource[]
*/
private $pipes;
/**
* @var resource
*/
private $process;
/**
* @var string[]
*/
private $outputValues;
/**
* @var int
*/
private $returnCode;
/**
* @var string
*/
private $cwd;
/**
* @param string $command
* @param string[] $arguments
*/
public function __construct($command, $arguments = [])
{
$this->command =
$command . ' ' .
implode(' ', $arguments);
$this->descriptorSpec = [];
$this->outputValues = [];
$this->pipes = [];
}
public function startForOutputCapture()
{
$this->descriptorSpec = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
];
$this->start();
// Set up output capturing.
foreach (array_keys($this->descriptorSpec) as $handle) {
$this->outputValues[$handle] = '';
}
}
/**
* @return int
*/
public function runAndCaptureOutput()
{
$this->startForOutputCapture();
$this->waitForExit();
return $this->returnCode;
}
/**
* Begins the process.
*/
public function start()
{
// Set up the argument list.
$arguments = [
$this->command,
$this->descriptorSpec,
&$this->pipes,
$this->cwd,
null,
['binary_pipes' => true, 'bypass_shell' => true]
];
// Call proc_open with the array. (We do it this way so we only need to create $arguments once.)
$this->process = call_user_func_array('proc_open', $arguments);
// Send the arguments into an exception if it didn't work.
if (!is_resource($this->process)) {
throw new \RuntimeException("Failed to start process with arguments: " . print_r($arguments, true));
}
}
/**
* Waits for process termination and determines its return code.
*
*/
public function waitForExit()
{
if ($this->process) {
$this->captureAnyOutput();
$this->returnCode = proc_close($this->process);
$this->process = null;
}
}
public function terminate()
{
if ($this->process) {
//proc_terminate does not work well on windows, so use taskkill
if (strncasecmp(PHP_OS, 'WIN', 3) == 0) {
$status = proc_get_status($this->process);
if ($status['running']) {
$this->returnCode = exec('taskkill /F /T /PID ' . $status['pid']);
}
} else {
$this->returnCode = proc_terminate($this->process);
}
$this->process = null;
}
}
/**
* @return boolean
*/
public function isRunning()
{
$statusArray = proc_get_status($this->process);
return $statusArray['running'];
}
/**
* @return int
*/
public function getReturnCode()
{
return $this->returnCode;
}
/**
* @return string
*/
public function getStandardOutResult()
{
return $this->getCaptureResult(1);
}
/**
* @return string
*/
public function getStandardErrorResult()
{
return $this->getCaptureResult(2);
}
/**
* @return string
*/
public function getCommand()
{
return $this->command;
}
/**
* @return string
*/
public function getCwd()
{
return $this->cwd;
}
/**
* @param string $cwd
*/
public function setCwd($cwd)
{
$this->cwd = $cwd;
}
/**
* @param int $index
* @return string
*/
private function getCaptureResult($index)
{
return
array_key_exists($index, $this->descriptorSpec) &&
$this->descriptorSpec[$index] == ['pipe', 'w']
? $this->outputValues[$index]
: null;
}
public function captureOutput()
{
foreach ($this->pipes as $handle => $pipe) {
// Read some stuff.
$result = stream_get_contents($pipe);
$this->outputValues[$handle] .= $result;
}
}
/**
* Captures all output and saves it to local variables.
*
*/
public function captureAnyOutput()
{
while (count($this->pipes) > 0) {
foreach ($this->pipes as $handle => $pipe) {
// Read some stuff.
$result = stream_get_contents($pipe);
$this->outputValues[$handle] .= $result;
// Mark and close the pipe if it's finished.
if (feof($pipe)) {
fclose($pipe);
unset($this->pipes[$handle]);
}
}
}
}
}
Content type
Image
Digest
Size
1.8 GB
Last updated
over 8 years ago
docker pull maindg/php-multi-process-failure