Search code examples
phpwindowsmultithreadingpthreadsproc-open

PHP: thread crash when reading pipe stream


I'm currently develpoing a deployment framework in PHP and got some problems regarding threads and streams.

I want to start a process, read its stdout and stderr (separately!), echo it and return the complete content of the streams when the process is terminated.

To get that functionaliy I'm using two threads, each is reading a different stream (stdout|stderr). Now the problem I got with this is that php is crashing when fgets gets called a second time. (Error code 0x5, Error offset 0x000610e7).

After a lot of trail and error I figured out that when I add a dummy array to the run function the crash is not always happening and it's working as expected. Has anyone an idea why this is happening?

I'm using Windows 7, PHP 5.4.22, MSVC9, pthreads 2.0.9

private static $pipeSpecsSilent = array(
   0 => array("pipe", "r"), // stdin
   1 => array("pipe", "w"), // stdout
   2 => array("pipe", "w")); // stderr

public function start()
{
    $this->procID = proc_open($this->executable, $this::$pipeSpecsSilent, $this->pipes);
    if (is_resource($this->procID))
    {
        $stdoutThread = new POutputThread($this->pipes[1]);
        $stderrThread = new POutputThread($this->pipes[2]);
        $stderrThread->start();
        $stdoutThread->start();

        $stdoutThread->join();
        $stderrThread->join();

        $stdout = trim($stdoutThread->getStreamValue());
        $stderr = trim($stderrThread->getStreamValue());

        $this->stop();
        return array('stdout' => $stdout, 'stderr' => $stderr);
    }
    return null;
}

/**
 * Closes all pipes and the process handle
 */
private function stop()
{
    for ($x = 0; $x < count($this->pipes); $x++)
    {
        fclose($this->pipes[$x]);
    }

    $this->resultValue = proc_close($this->procID);
}


class POutputThread extends Thread
{
    private $pipe;
    private $content;

    public function __construct($pipe)
    {
        $this->pipe = $pipe;
    }

    public function run()
    {
        $content = '';

        // this line is requires as we get a crash without it.
        // it seems like there is something odd happening?
        $stackDummy = array('', '');

        while (($line = fgets($this->pipe)))
        {
            PLog::i($line);
            $content .= $line;
        }
        $this->content = $content;
    }

    /**
     * Returns the value of the stream that was read
     *
     * @return string
     */
    public function getStreamValue()
    {
        return $this->content;
    }
}

Solution

  • I found the problem:

    Although I'm closing the streams after all threads are terminated it seems like it is required to close the stream inside the thread that was reading from it. So I replaced the $this->stop(); call with a fclose($this->pipe); inside the run function and everything works just fine.