-
-
Save anvius/9000293 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require "thread.php"; | |
function doSomething($res, $t) { | |
usleep($t); | |
exit($res); | |
} | |
$thread1 = new Thread('doSomething'); | |
$thread2 = new Thread('doSomething'); | |
$thread3 = new Thread('doSomething'); | |
$thread1->start(3, 10); | |
$thread2->start(2, 40); | |
$thread3->start(1, 30); | |
while ($thread1->isAlive(1) || $thread2->isAlive(2) || $thread3->isAlive(3)); | |
echo "Thread 1 exit code (should be 3): " . $thread1->getExitCode() . "\n"; | |
echo "Thread 2 exit code (should be 2): " . $thread2->getExitCode() . "\n"; | |
echo "Thread 3 exit code (should be 1): " . $thread3->getExitCode() . "\n"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Implements threading in PHP | |
* | |
* @package <none> | |
* @version 1.0.0 - stable | |
* @author Tudor Barbu <[email protected]> | |
* @copyright MIT | |
*/ | |
class Thread { | |
const FUNCTION_NOT_CALLABLE = 10; | |
const COULD_NOT_FORK = 15; | |
/** | |
* possible errors | |
* | |
* @var array | |
*/ | |
private $errors = array( | |
Thread::FUNCTION_NOT_CALLABLE => 'You must specify a valid function name that can be called from the current scope.', | |
Thread::COULD_NOT_FORK => 'pcntl_fork() returned a status of -1. No new process was created', | |
); | |
/** | |
* callback for the function that should | |
* run as a separate thread | |
* | |
* @var callback | |
*/ | |
protected $runnable; | |
/** | |
* holds the current process id | |
* | |
* @var integer | |
*/ | |
private $pid; | |
/** | |
* hodls exit code after child die | |
*/ | |
private $exitCode = -1; | |
/** | |
* checks if threading is supported by the current | |
* PHP configuration | |
* | |
* @return boolean | |
*/ | |
public static function available() { | |
$required_functions = array( | |
'pcntl_fork', | |
); | |
foreach( $required_functions as $function ) { | |
if ( !function_exists( $function ) ) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* class constructor - you can pass | |
* the callback function as an argument | |
* | |
* @param callback $_runnable | |
*/ | |
public function __construct( $_runnable = null ) { | |
if( $_runnable !== null ) { | |
$this->setRunnable( $_runnable ); | |
} | |
} | |
/** | |
* sets the callback | |
* | |
* @param callback $_runnable | |
* @return callback | |
*/ | |
public function setRunnable( $_runnable ) { | |
if( self::runnableOk( $_runnable ) ) { | |
$this->runnable = $_runnable; | |
} | |
else { | |
throw new Exception( $this->getError( Thread::FUNCTION_NOT_CALLABLE ), Thread::FUNCTION_NOT_CALLABLE ); | |
} | |
} | |
/** | |
* gets the callback | |
* | |
* @return callback | |
*/ | |
public function getRunnable() { | |
return $this->runnable; | |
} | |
/** | |
* checks if the callback is ok (the function/method | |
* actually exists and is runnable from the current | |
* context) | |
* | |
* can be called statically | |
* | |
* @param callback $_runnable | |
* @return boolean | |
*/ | |
public static function runnableOk( $_runnable ) { | |
return ( function_exists( $_runnable ) && is_callable( $_runnable ) ); | |
} | |
/** | |
* returns the process id (pid) of the simulated thread | |
* | |
* @return int | |
*/ | |
public function getPid() { | |
return $this->pid; | |
} | |
/** | |
* checks if the child thread is alive | |
* | |
* @return boolean | |
*/ | |
public function isAlive() { | |
$pid = pcntl_waitpid( $this->pid, $status, WNOHANG ); | |
if ($pid === 0) { // child is still alive | |
return true; | |
} else { | |
if (pcntl_wifexited($status) && $this->exitCode == -1) { // normal exit | |
$this->exitCode = pcntl_wexitstatus($status); | |
} | |
return false; | |
} | |
} | |
/** | |
* return exit code of child (-1 if child is still alive) | |
* | |
* @return int | |
*/ | |
public function getExitCode() { | |
$this->isAlive(); | |
return $this->exitCode; | |
} | |
/** | |
* starts the thread, all the parameters are | |
* passed to the callback function | |
* | |
* @return void | |
*/ | |
public function start() { | |
$pid = @ pcntl_fork(); | |
if( $pid == -1 ) { | |
throw new Exception( $this->getError( Thread::COULD_NOT_FORK ), Thread::COULD_NOT_FORK ); | |
} | |
if( $pid ) { | |
// parent | |
$this->pid = $pid; | |
} | |
else { | |
// child | |
pcntl_signal( SIGTERM, array( $this, 'signalHandler' ) ); | |
$arguments = func_get_args(); | |
if ( !empty( $arguments ) ) { | |
call_user_func_array( $this->runnable, $arguments ); | |
} | |
else { | |
call_user_func( $this->runnable ); | |
} | |
exit( 0 ); | |
} | |
} | |
/** | |
* attempts to stop the thread | |
* returns true on success and false otherwise | |
* | |
* @param integer $_signal - SIGKILL/SIGTERM | |
* @param boolean $_wait | |
*/ | |
public function stop( $_signal = SIGKILL, $_wait = false ) { | |
if( $this->isAlive() ) { | |
posix_kill( $this->pid, $_signal ); | |
if( $_wait ) { | |
pcntl_waitpid( $this->pid, $status = 0 ); | |
} | |
} | |
} | |
/** | |
* alias of stop(); | |
* | |
* @return boolean | |
*/ | |
public function kill( $_signal = SIGKILL, $_wait = false ) { | |
return $this->stop( $_signal, $_wait ); | |
} | |
/** | |
* gets the error's message based on | |
* its id | |
* | |
* @param integer $_code | |
* @return string | |
*/ | |
public function getError( $_code ) { | |
if ( isset( $this->errors[$_code] ) ) { | |
return $this->errors[$_code]; | |
} | |
else { | |
return 'No such error code ' . $_code . '! Quit inventing errors!!!'; | |
} | |
} | |
/** | |
* signal handler | |
* | |
* @param integer $_signal | |
*/ | |
protected function signalHandler( $_signal ) { | |
switch( $_signal ) { | |
case SIGTERM: | |
exit( 0 ); | |
break; | |
} | |
} | |
} | |
// EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment