Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 41 |
| MultiProcessor | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
182 | |
0.00% |
0 / 41 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| run | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 21 |
|||
| addChild | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| startChildren | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 7 |
|||
| forkChild | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 10 |
|||
| <?php | |
| /** | |
| * 並列実行ドライバ | |
| * | |
| * Linuxの fork&exec を利用したものなのでWindowsでは動作しない。 | |
| * pcntl拡張が必要。 | |
| * | |
| * @uses pcntl | |
| */ | |
| class MultiProcessor | |
| { | |
| /** | |
| * 登録された子プロセスのリスト | |
| * | |
| * <pre> | |
| * executable => PATH/TO/EXECUTABLE | |
| * args => array(arg1, arg2...) | |
| * </pre> | |
| * @var array | |
| */ | |
| private $childProcesses = array(); | |
| /** | |
| * 実行中の子プロセスのリスト | |
| * <pre> | |
| * pid => forked => true/false | |
| * args => array(arg1, arg2...) | |
| * </pre> | |
| * @var array | |
| */ | |
| private $pids = array(); | |
| /** | |
| * | |
| */ | |
| public function __construct() { | |
| } | |
| /** | |
| * 登録された子プロセスを fork&exec する | |
| * | |
| * @return bool 子プロセス全てが正常終了(0)したか否か | |
| * @throws Exception pcntl拡張がインストールされていない | |
| * @throws Exception fork失敗 | |
| */ | |
| public function run() { | |
| if(!function_exists('pcntl_fork')) { | |
| throw new Exception('pcntl extension not installed.'); | |
| } | |
| // --enable-sigchild のハンドラに食われないよう自前でブロック | |
| pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)); | |
| if(count($this->childProcesses) === 0) { | |
| return false; | |
| } | |
| // fork & exec | |
| $this->startChildren(); | |
| // wait | |
| $arrExited = array(); | |
| foreach($this->pids as $pid=>$dummy) { | |
| pcntl_waitpid($pid, $status, WUNTRACED); | |
| //echo $pid, ":", pcntl_wexitstatus($status), "\n"; | |
| $arrExited[$pid] = pcntl_wexitstatus($status); | |
| } | |
| foreach($arrExited as $pid=>$status) { | |
| if($status !== 0) { | |
| return false; | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * 子プロセスを登録する | |
| * | |
| * $executableFullpath は実行可能でなければならない。 | |
| * interpreter, permissionをよく確認すること。 | |
| * | |
| * @param string $executableFullpath 実行可能ファイルのfullpath | |
| * @param string[] $args (optional) 引数 | |
| */ | |
| public function addChild($executableFullpath, array $args=array()) { | |
| $this->childProcesses[] = array('executable'=>$executableFullpath, 'args'=>$args); | |
| } | |
| protected function startChildren() { | |
| $numOfChildren = count($this->childProcesses); | |
| foreach($this->childProcesses as $child) { | |
| $childPid = $this->forkChild($child['executable'], $child['args']); | |
| $this->pids[$childPid]['forked'] = true; | |
| $this->pids[$childPid]['args'] = $child['args']; | |
| } | |
| } | |
| /** | |
| * fork & exec | |
| * | |
| * オーバレイをここでカプセル | |
| * @param string $executable | |
| * @param string[] $args | |
| * @return int process id | |
| * @throws \Exception fork failed | |
| */ | |
| protected function forkChild($executable, $args) { | |
| $pid = pcntl_fork(); | |
| if ($pid === -1) { | |
| throw new \Exception('Fork failed.'); | |
| } elseif ($pid > 0) { | |
| // Parent | |
| //echo "Forking child...\n"; | |
| } else { | |
| // Child | |
| pcntl_exec($executable, $args); | |
| // ここに来たということは pcntl_exec 失敗 | |
| exit(255); | |
| } | |
| return $pid; | |
| } | |
| } |