Last active
August 29, 2015 14:27
-
-
Save greevex/0e050b8bf2358b121618 to your computer and use it in GitHub Desktop.
Server RAM test
This file contains 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
#!/usr/bin/env php | |
<?php | |
namespace hardcore; | |
abstract class appBase | |
{ | |
private $input; | |
abstract protected function handle(); | |
public function run() | |
{ | |
error_log('Starting application...'); | |
$this->handle(); | |
error_log('Ending application...'); | |
} | |
protected function getInput($name) | |
{ | |
if($this->input === null) { | |
$this->input = $this->importArgs($GLOBALS['argv']); | |
} | |
return isset($this->input[$name]) ? $this->input[$name] : null; | |
} | |
/** | |
* Function to parse php arguments | |
* | |
* @param array $argv | |
* @return array | |
*/ | |
private function importArgs($argv) { | |
array_shift($argv); | |
$out = array(); | |
foreach ($argv as $arg){ | |
if (substr($arg,0,2) == '--'){ | |
$eqPos = strpos($arg,'='); | |
if ($eqPos === false){ | |
$key = substr($arg,2); | |
$out[$key] = isset($out[$key]) ? $out[$key] : true; | |
} else { | |
$key = substr($arg,2,$eqPos-2); | |
$out[$key] = substr($arg,$eqPos+1); | |
} | |
} elseif (substr($arg,0,1) == '-'){ | |
if (substr($arg,2,1) == '='){ | |
$key = substr($arg,1,1); | |
$out[$key] = substr($arg,3); | |
} else { | |
$chars = str_split(substr($arg,1)); | |
foreach ($chars as $char){ | |
$key = $char; | |
$out[$key] = isset($out[$key]) ? $out[$key] : true; | |
} | |
} | |
} elseif (substr($arg,0,1) == ']'){ | |
list($key, $value) = explode('=', $arg); | |
$out[$key] = $value; | |
} else { | |
$out[] = $arg; | |
} | |
} | |
return $out; | |
} | |
} | |
class hardcore | |
extends appBase | |
{ | |
/** | |
* Handle function | |
* | |
* @return mixed | |
*/ | |
protected function handle() | |
{ | |
if($this->getInput('help')) { | |
echo <<<TEXT | |
======= | |
PARAMS: | |
--max-tests=... (integer) Count of internal iterations (each iteration cost some RAM) | |
--max-threads=... (integer) Count of process threads (forks) | |
TEXT; | |
return; | |
} | |
for(;;) { | |
$this->threadPool()->refresh(); | |
while($this->threadPool()->getPoolCount() < $this->threadPool()->getMaxThreads()) { | |
$this->threadPool()->add(function() { | |
$this->process(); | |
}); | |
} | |
sleep(1); | |
} | |
} | |
protected function process() | |
{ | |
$maxTests = (int)$this->getInput('max-tests'); | |
for(;;) { | |
$strings = []; | |
for ($i = 0; $i < $maxTests; $i++) { | |
$strings[] = str_repeat(md5(mt_rand(0,5)) . md5(mt_rand(6,10)) . md5(mt_rand(11,15)) . md5(mt_rand(15,20)) . md5(mt_rand(21,25)), 128); | |
usleep(250); | |
} | |
unset($strings); | |
} | |
} | |
/** | |
* @return threadPool | |
*/ | |
protected function threadPool() | |
{ | |
static $threadPool; | |
if(!isset($threadPool)) { | |
$threadPool = new threadPool(); | |
$queue = 0; | |
$threads = (int)$this->getInput('max-threads'); | |
if(!$threads) { | |
$threads = 100; | |
} | |
error_log("Instantiating threadPool with queue: {$queue} and threads: {$threads}"); | |
$threadPool->setMaxQueue($queue); | |
$threadPool->setMaxThreads($threads); | |
} | |
return $threadPool; | |
} | |
} | |
class thread | |
{ | |
/** | |
* Status code | |
* Function is not callable | |
*/ | |
const FUNCTION_NOT_CALLABLE = 10; | |
/** | |
* Status code | |
* Couldn't fork | |
*/ | |
const COULD_NOT_FORK = 15; | |
/** | |
* Status code | |
* Fork ready | |
*/ | |
const FORK_READY = -50; | |
/** | |
* Possible errors | |
* | |
* @var array | |
*/ | |
private $errors = [ | |
self::FUNCTION_NOT_CALLABLE => 'You must specify a valid function name that can be called from the current scope.', | |
self::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 callable | |
*/ | |
protected $runnable; | |
/** | |
* holds the current process id | |
* | |
* @var integer | |
*/ | |
private $pid; | |
/** | |
* 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 callable $callable | |
*/ | |
public function __construct($callable = null) | |
{ | |
if($callable !== null) { | |
$this->setRunnable($callable); | |
} | |
} | |
/** | |
* Set callback | |
* | |
* @param callable $runnable | |
*/ | |
public function setRunnable($runnable) | |
{ | |
$this->runnable = $runnable; | |
} | |
/** | |
* Get callback | |
* | |
* @return callable | |
*/ | |
public function getRunnable() | |
{ | |
return $this->runnable; | |
} | |
/** | |
* returns the process id (pid) of the simulated thread | |
* | |
* @return int pid | |
*/ | |
public function getPid() | |
{ | |
return $this->pid; | |
} | |
/** | |
* checks if the child thread is alive | |
* | |
* @return boolean | |
*/ | |
public function isAlive() | |
{ | |
if(!isset($this->pid)) { | |
return false; | |
} | |
$pid = pcntl_waitpid( $this->pid, $status, WNOHANG ); | |
return ( $pid === 0 ); | |
} | |
/** | |
* starts the thread, all the parameters are | |
* passed to the callback function | |
* | |
* @return thread | |
* @throws \Exception | |
*/ | |
public function start() | |
{ | |
$pid = @ pcntl_fork(); | |
if( $pid == -1 ) { | |
throw new \Exception( $this->getError( self::COULD_NOT_FORK ), self::COULD_NOT_FORK ); | |
} | |
if( $pid ) { | |
$this->pid = $pid; | |
} | |
else { | |
$arguments = func_get_args(); | |
pcntl_signal(SIGTERM, array(__CLASS__, 'signalHandler')); | |
register_shutdown_function(array(__CLASS__, 'signalHandler')); | |
pcntl_signal_dispatch(); | |
call_user_func_array($this->runnable, $arguments); | |
exit(0); | |
} | |
return $this; | |
} | |
/** | |
* 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 ) | |
{ | |
$isAlive = (int)$this->isAlive(); | |
error_log("Stopping process {$this->pid}, alive:{$isAlive}"); | |
if($isAlive) { | |
posix_kill( $this->pid, $_signal ); | |
if( $_wait ) { | |
pcntl_waitpid( $this->pid, $status = 0 ); | |
} | |
} | |
} | |
/** | |
* alias of stop(); | |
* | |
* @param int $_signal | |
* @param bool $_wait | |
* | |
* @return bool | |
*/ | |
public function kill( $_signal = SIGKILL, $_wait = false ) | |
{ | |
error_log("Killing process with pid {$this->pid}..."); | |
for($i = 0; $i < 10; $i++) { | |
posix_kill( $this->pid, $_signal ); | |
usleep(10000); | |
} | |
if( $_wait ) { | |
error_log("Waiting process [pid {$this->pid}]..."); | |
pcntl_waitpid( $this->pid, $status = 0 ); | |
} | |
} | |
/** | |
* 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 | |
*/ | |
public static function signalHandler($_signal = SIGTERM) | |
{ | |
switch($_signal) { | |
case SIGTERM: | |
exit(0); | |
break; | |
} | |
} | |
} | |
/** | |
* Thread Pool class | |
* | |
* Organize thread pool with queue | |
*/ | |
class threadPool | |
{ | |
/** | |
* Max treads in pool | |
* | |
* @var int | |
*/ | |
private $max_threads = 5; | |
/** | |
* Max queue length | |
* | |
* @var int | |
*/ | |
private $max_queue = 5; | |
/** | |
* Pool queue array | |
* | |
* @var array | |
*/ | |
private $queue = array(); | |
/** | |
* Pool array | |
* | |
* @var thread[] | |
*/ | |
private $pool = array(); | |
/** | |
* Result array | |
* | |
* @var array | |
*/ | |
private $result = array(); | |
/** | |
* Check if has alive threads | |
* | |
* @return bool | |
*/ | |
public function hasAlive() | |
{ | |
$this->refresh(); | |
return (count($this->pool) > 0 || count($this->queue) > 0); | |
} | |
/** | |
* Wait for alive threads | |
* | |
* @return bool | |
*/ | |
public function waitAll() | |
{ | |
while($this->hasAlive()) { | |
$this->refresh(); | |
usleep(100000); | |
} | |
return true; | |
} | |
/** | |
* Refresh threads in pool | |
* | |
* @return bool | |
*/ | |
public function refresh() | |
{ | |
/** | |
* @note kill thread if thread is not alive | |
*/ | |
foreach($this->pool as $thread_key => $thread) { | |
if(!$thread->isAlive()) { | |
$thread->kill(); | |
unset($this->pool[$thread_key]); | |
} | |
} | |
/** | |
* @note add threads in pool from queue | |
*/ | |
while(count($this->queue) > 0 && count($this->pool) < $this->max_threads) { | |
list($thread, $params) = array_shift($this->queue); | |
/** @var thread $thread */ | |
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */ | |
$this->pool[] = $thread->start($params); | |
} | |
return true; | |
} | |
/** | |
* Get pool count | |
* | |
* @return int | |
*/ | |
public function getPoolCount() | |
{ | |
return is_array($this->pool) ? count($this->pool) : 0; | |
} | |
/** | |
* Add thread with arguments in pool | |
* | |
* @param callable $thread | |
* @param array|null $arguments | |
* @return bool | |
*/ | |
public function add($thread, $arguments = null) | |
{ | |
if(count($this->pool) >= $this->max_threads && count($this->queue) >= $this->max_queue) { | |
return false; | |
} | |
$this->queue[] = array(new thread($thread), $arguments); | |
$this->refresh(); | |
return true; | |
} | |
/** | |
* Set max queue length | |
* | |
* @param $max_queue | |
* @return mixed | |
*/ | |
public function setMaxQueue($max_queue) | |
{ | |
return $this->max_queue = $max_queue; | |
} | |
/** | |
* Get max queue length | |
* | |
* @return int | |
*/ | |
public function getMaxQueue() | |
{ | |
return $this->max_queue; | |
} | |
/** | |
* Set max threads length | |
* | |
* @param int $max_threads | |
* @return mixed | |
*/ | |
public function setMaxThreads($max_threads) | |
{ | |
return $this->max_threads = $max_threads; | |
} | |
/** | |
* Get max threads value | |
* | |
* @return int max threads | |
*/ | |
public function getMaxThreads() | |
{ | |
return $this->max_threads; | |
} | |
/** | |
* Get thread pool | |
* | |
* @return array pool | |
*/ | |
public function getPool() | |
{ | |
return $this->pool; | |
} | |
/** | |
* Get threads work results | |
* | |
* @return array threads work results | |
*/ | |
public function getResults() | |
{ | |
$result = $this->result; | |
$this->result = array(); | |
return $result; | |
} | |
} | |
$app = new hardcore(); | |
$app->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment