| 
<?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 ([email protected])
 * 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;
 
 }
 }
 
 ?>
 
 |