Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 41
MultiProcessor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 5
182.00
0.00% covered (danger)
0.00%
0 / 41
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 run
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 21
 addChild
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 startChildren
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 7
 forkChild
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
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;
    }
}