PHP proc_open opens multiple times
Asked Answered
S

3

8

I have a utility function used for executing a program through CLI (cmd, bash etc). It returns an array of 3 items: STDOUT, STDERR and EXIT CODE.

So far, it's been working nicely without issues. In fact, the problem I have with it doesn't really hinder it's functionality, but I'm concerned about performance.

The problem is that in certain cases, PHP runs the same command multiple times (3 times in my case), even if it was supposed to only do this once.

/**
 * Executes a program and waits for it to finish, taking pipes into account.
 * @param string $cmd Command line to execute, including any arguments.
 * @param string $input Data for standard input.
 * @param boolean $log Whether to log execution failures or not (defaults to true).
 * @return array Array of "stdout", "stderr" and "return".
 */
public static function execute($cmd,$stdin=null,$log=true){
    //static $once=true; if(!$once)die; $once=false;
    $proc=proc_open($cmd, array(
        0=>array('pipe','r'),
        1=>array('pipe','w'),
        2=>array('pipe','w')   ), $pipes);
    fwrite($pipes[0],$stdin);                fclose($pipes[0]);
    $stdout=stream_get_contents($pipes[1]);  fclose($pipes[1]);
    $stderr=stream_get_contents($pipes[2]);  fclose($pipes[2]);
    $return=proc_close($proc);
    if($return!=0 && $log)
        xlog('Error: Program execution returned failure.',$stdout,$stderr,$return);
    return array( 'stdout'=>$stdout, 'stderr'=>$stderr, 'return'=>$return );
}

Note the commented line (line 9). That was for testing. I enabled it to ensure the target program only runs once (I was thinking my code may be calling the same function somehow). But even with that line enabled, the program still ran multiple times.

As it is, I have 2 places in my code where I'm executing the same program (on different occasions). The command line is the same for both of them.

However, on one occasion, the program runs once, while in the occasion, PHP runs the program 3 times.

I've been monitoring and seeing this behavior under Process Explorer. I'm using Windows 7 x64. The program is 32 bit, as is PHP.

Edit: The program in question is custom developed, and it doesn't open new processes.

Skill answered 23/1, 2011 at 20:38 Comment(8)
Use another process tool to verify the observation. You didn't mention what program it is (might fork into subprocesses by itself).Mardis
@Christian: How can we check it? As you said, you didn't mention what program it is. Mario is completely right; you should listen to him.Spirelet
Sorry, I meant "checked it" (I've now deleted the comment to avoid confusion). What I (meant) is that I checked it with Process Monitor. Edit: And I did listen to him ;) lolSkill
Can you provide us with case-by-case examples, and how many time the script executes for each case? Make sure to also note the time you executed the script. It's my fear that the repeats can vary even with the same input.Ourselves
You could use something like flock() php.net/manual/en/function.flock.php to ensure it's only running once.Floorage
Perhaps log pids retrieved from getmypid(), see if it's a single process running the method 3 times, or 3 seperate processes running the method once each.Marela
@MarcB - It the same process running the executable 3 times.Skill
I'm having the similar effect with a fastcgi client in PHP that runs another PHP code with shell_exec: I've got 32 executions in parallel of php-cgi (32 is the number of fastcgi PHP processes), and all are failing (execution never ends). My solution was to run the sub codes with a new call to the fastcgi client, avoiding multiple cascade of shells and fastcgi clients.Insignificance
H
1

Your code to test that it is run only once looks flawed.

If you have 2 php processes running they will not share a static variable. So it could be possible you have having simultaneous requests causing this to run more than once.

Second, you should be setting $once to false at the end of the function, otherwise the die will never be reached.

Try adding some logging to see if the function is being called from twice.

Create some unit/stress tests that just run the external application. If you see multiple processes, then there is something with your app that is wrong, not the php code.

Hirza answered 15/5, 2011 at 21:45 Comment(6)
I'm running this locally, hence I can see each and every session as well as processes opening up. The program runs only once when invoked by itself. I'm positive that my original statement is correct.Skill
@Skill - When you add logging to the function, does it appear to be called multiple times, or is the process itself started multiple times by php windows or the app?Hirza
@ByronWhitlock - The process itself is loaded multiple times. The commented code ensures the function is only called once.Skill
@Skill Scinerras: you do know that the static variable $once exists separately for each process, with potentially a different value in each? i.e. that line of code won't stop different processes from running your functionAparejo
@Aparejo - But I'm positive there's only one process running the function. That code ensures the function is only called once per process.Skill
@Christian: yep that's all i meant. As long as you know :)Aparejo
D
0

It is very strange. And It is very hard to figure out without the full code.

If you are stressing your server calling the same page several times, my best bet it is probably related to the CPU round robin process. The PHP does not have time to set the static variable to false because at the same there are an another request to this method. Other possibility is the PHP is failing to isolate properly the static value and the different request to this method can read different memory position until the PHP sinchronize the values.

Duologue answered 17/5, 2011 at 5:26 Comment(0)
D
0

I know this may not be the best option, but this is what I did. Though this was on Linux, but I'm sure there is way to port this to Windows.

What I did, was run pgrep and check if there already existed a command like this and if it did simply exit. As you said you are running same command (with exact parameters), so just check if there already is a command running and act respectively.

I used this command:

$pid = shell_exec('pgrep -cfx "/* My command */"');
if ($pid > 1) return -1;
Despain answered 17/5, 2011 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.