<?php /** * PHP_Fork class usage examples * ================================================================================== * NOTE: In real world you surely want to keep each class into * a separate file, then include() it into your application. * For this examples is more useful to keep all_code_into_one_file, * so that each example shows a unique feature of the PHP_Fork framework. * ================================================================================== * simple_controller.php * * This example shows how to attach a controller to started pseudo-threads * Forked processes can sometime die or defunct for many reasons... * Without a controller process that periodically check thread status we * can not ensure that started threads are still alive into system * Here we have n executeThreads and a controllerThread that check all them * when the controller detect that a thread is died it try to respawn it. * * ATTENTION: this example often fails on Cygwin platform, probably due to some bugs * into ipc implementations that prevent the shared memory to be totally released * when a process die. * * ================================================================================== * * Copyright (c) 2003-2002 by Luca Mariano (luca.mariano@email.it) * http://www.lucamariano.it * * This program is free software. You can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ // Import of base class require_once ("../Fork.php"); // number of executeThreads we want define ("NUM_THREAD", 2); // max delay (seconds) that controller can accept from child's ping define ("CTRL_MAX_IDLE", 3); // controller will check threads status every CTRL_POLLING_INTERVAL secs. define ("CTRL_POLLING_INTERVAL", 5); // The same as previos examples, with the add of the controller ping... class executeThread extends PHP_Fork { var $counter;
function executeThread($name) { $this->PHP_Fork($name); $this->counter = 0; }
function run() { while (true) { // setVariable is a method inherited from PHP_Fork super class // it sets a variable that can be accessed thru its name // by parent process $this->setVariable('counter', $this->counter++); /** * While using the controller, every child must notify to controller that it's alive * The frequency of this notify must be set according to the parameter $maxIdleTime * (controllerThread constructor, default is 60 sec.) * Typically executeThreads runs an infinite loop making something useful; you should * calculate a reasonable time for one loop, then set the controller's $maxIdleTime * to a superior value. * Into the while(true) { } loop of the thread insert a call to the "setAlive" method * so that controller can detect properly the status of child processes. */ $this->setAlive(); sleep(1); } }
function getCounter() { // parent process can call this facility method // in order to get back the actual value of the counter return $this->getVariable('counter'); } }
class controllerThread extends PHP_Fork { var $_sleepInt; var $_executeThreadPool; var $_maxIdleTime; var $_respawnThread;
/** * controllerThread::controllerThread() * * @param $name * @param $executeThreadPool * @param $maxIdleTime * @param $interval * @return */ function controllerThread($name, &$executeThreadPool, $maxIdleTime = 60, $interval = 60) { $this->PHP_Fork($name); $this->_sleepInt = $interval; $this->_executeThreadPool = &$executeThreadPool; $this->_maxIdleTime = $maxIdleTime; $this->_respawnThread = array();
$this->setVariable('_executeThreadPool', $this->_executeThreadPool); }
function run() { while (true) { $this->_detectDeadChilds(); $this->_respawnDeadChilds();
sleep($this->_sleepInt); } } function stopAllThreads() { $this->_executeThreadPool = $this->getThreadPool(); foreach($this->_executeThreadPool as $thread) { $thread->stop(); echo "Stopped " . $thread->getName() . "\n"; } unset($this->_executeThreadPool); $this->_executeThreadPool = array(); $this->setVariable('_executeThreadPool', $this->_executeThreadPool); }
function getThreadPool() { return $this->getVariable('_executeThreadPool'); }
function _detectDeadChilds() { // check every executethread to see if it is alive... foreach ($this->_executeThreadPool as $idx => $executeThread) { if ($executeThread->getLastAlive() > $this->_maxIdleTime) { // this thread is not responding, probably [defunct] $threadName = $executeThread->getName(); print time() . "-" . $this->getName() . "-" . $threadName . " seems to be died...\n"; // so let's kill it... $executeThread->stop();
unset($executeThread); // remove this thread from the pool array_splice($this->_executeThreadPool, $idx, 1); // and add them to the "to be respawned" thread list... $this->_respawnThread[] = $threadName; // stop this foreach break; } } }
function _respawnDeadChilds() { foreach ($this->_respawnThread as $idx => $threadName) { $n = &new executeThread ($threadName); // usually we try to start a Thread without this check // if Shared Memory Area is not ready, the start() method // die, so the process is destroyed. When respawing a dead child // this is not useful, because die() will cause the controller itself // to die! // So let's check if IPC is ok before call the start() method. if ($n->_ipc_is_ok) { $n->start(); $this->_executeThreadPool[] = &$n; print time() . "-" . $this->getName() . "- New instance of " . $threadName . " successfully spawned (PID=" . $n->getPid() . ")\n"; array_splice($this->_respawnThread, $idx, 1); $this->setVariable('_executeThreadPool', $this->_executeThreadPool); } else { print time() . "-" . $this->getName() . "-" . "Unable to create IPC segment...\n"; } } } }
/** * Functions used by the console */ function _getInputCLI() { $opt = _read(); $opt = strtoupper (trim($opt)); return $opt; }
function _read() { $fp = fopen("php://stdin", "r"); $input = fgets($fp, 255); fclose($fp);
return $input; }
/** * Main program. Bring up two instances of the executeThread class that * runs concurrently. It's a multi-thread app with a few lines of code!!! * executeThread does nothing interesting, it simply has a counter and increment * this counter each second... (see class definition at top of this file) */ for ($i = 0;$i < NUM_THREAD;$i++) { $executeThread[$i] = &new executeThread ("executeThread-" . $i); $executeThread[$i]->start(); echo "Started " . $executeThread[$i]->getName() . " with PID " . $executeThread[$i]->getPid() . "...\n"; }
$ctrl = new controllerThread("controllerThread", $executeThread, CTRL_MAX_IDLE, CTRL_POLLING_INTERVAL); $ctrl->start(); print "Started " . $ctrl->getName() . " with PID " . $ctrl->getPid() . "...\n\n";
echo "This is the main process.\nPress [X] to terminate, [G] to read thread's counter.\nTo test the controller respawn functionality, simply kill one (or more) executeThread (kill -9 [pid]). The controller should detect the died child & respawn it very soon...\n";
/** * Console simple listener */ while (true) { echo ">";
$opt = _getInputCLI(); echo "\n"; switch ($opt) { case "X": // when using the controller we cannot traverse the $executeThread array // because it only contains the original pool of thread. // if a thread dies, the controller respawn it and updates it's own // thread pool. The method stopAllThreads() stops all running threads. $ctrl->stopAllThreads(); // stop the controller itself $ctrl->stop(); print "Stopped " . $ctrl->getName() . "\n"; exit; break;
case "G": $pool = $ctrl->getThreadPool(); foreach ($pool as $thread) { //print_r($thread); echo $thread->getName() . " returns " . $thread->getCounter() . "\n"; } break;
} }
?>
|