Improved threading and resource usage, new defaults

This commit is contained in:
Shoghi Cervantes 2015-01-02 18:46:39 +01:00
parent 47cbf56725
commit 692045d714
No known key found for this signature in database
GPG Key ID: 78464DB0A7837F89
10 changed files with 163 additions and 68 deletions

View File

@ -1465,22 +1465,6 @@ class Server{
$this->dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR; $this->dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
$this->pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR; $this->pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
$this->entityMetadata = new EntityMetadataStore();
$this->playerMetadata = new PlayerMetadataStore();
$this->levelMetadata = new LevelMetadataStore();
$this->operators = new Config($this->dataPath . "ops.txt", Config::ENUM);
$this->whitelist = new Config($this->dataPath . "white-list.txt", Config::ENUM);
if(file_exists($this->dataPath . "banned.txt") and !file_exists($this->dataPath . "banned-players.txt")){
@rename($this->dataPath . "banned.txt", $this->dataPath . "banned-players.txt");
}
@touch($this->dataPath . "banned-players.txt");
$this->banByName = new BanList($this->dataPath . "banned-players.txt");
$this->banByName->load();
@touch($this->dataPath . "banned-ips.txt");
$this->banByIP = new BanList($this->dataPath . "banned-ips.txt");
$this->banByIP->load();
$this->consoleThreaded = new \Threaded(); $this->consoleThreaded = new \Threaded();
$this->console = new CommandReader($this->consoleThreaded); $this->console = new CommandReader($this->consoleThreaded);
@ -1532,6 +1516,22 @@ class Server{
$this->rcon = new RCON($this, $this->getConfigString("rcon.password", ""), $this->getConfigInt("rcon.port", $this->getPort()), ($ip = $this->getIp()) != "" ? $ip : "0.0.0.0", $this->getConfigInt("rcon.threads", 1), $this->getConfigInt("rcon.clients-per-thread", 50)); $this->rcon = new RCON($this, $this->getConfigString("rcon.password", ""), $this->getConfigInt("rcon.port", $this->getPort()), ($ip = $this->getIp()) != "" ? $ip : "0.0.0.0", $this->getConfigInt("rcon.threads", 1), $this->getConfigInt("rcon.clients-per-thread", 50));
} }
$this->entityMetadata = new EntityMetadataStore();
$this->playerMetadata = new PlayerMetadataStore();
$this->levelMetadata = new LevelMetadataStore();
$this->operators = new Config($this->dataPath . "ops.txt", Config::ENUM);
$this->whitelist = new Config($this->dataPath . "white-list.txt", Config::ENUM);
if(file_exists($this->dataPath . "banned.txt") and !file_exists($this->dataPath . "banned-players.txt")){
@rename($this->dataPath . "banned.txt", $this->dataPath . "banned-players.txt");
}
@touch($this->dataPath . "banned-players.txt");
$this->banByName = new BanList($this->dataPath . "banned-players.txt");
$this->banByName->load();
@touch($this->dataPath . "banned-ips.txt");
$this->banByIP = new BanList($this->dataPath . "banned-ips.txt");
$this->banByIP->load();
$this->maxPlayers = $this->getConfigInt("max-players", 20); $this->maxPlayers = $this->getConfigInt("max-players", 20);
$this->setAutoSave($this->getConfigBoolean("auto-save", true)); $this->setAutoSave($this->getConfigBoolean("auto-save", true));
@ -2134,7 +2134,7 @@ class Server{
public function sendUsage(){ public function sendUsage(){
if($this->lastSendUsage instanceof SendUsageTask){ if($this->lastSendUsage instanceof SendUsageTask){
if(!$this->lastSendUsage->isFinished()){ //do not call multiple times if(!$this->lastSendUsage->isGarbage()){ //do not call multiple times
return; return;
} }
} }
@ -2146,7 +2146,7 @@ class Server{
} }
$version = new VersionString(); $version = new VersionString();
$this->lastSendUsage = new SendUsageTask("http://stats.pocketmine.net/usage.php", [ $this->lastSendUsage = new SendUsageTask("https://stats.pocketmine.net/usage.php", [
"serverid" => $this->serverID, "serverid" => $this->serverID,
"port" => $this->getPort(), "port" => $this->getPort(),
"os" => Utils::getOS(), "os" => Utils::getOS(),

View File

@ -26,9 +26,13 @@ namespace pocketmine;
*/ */
abstract class Thread extends \Thread{ abstract class Thread extends \Thread{
public final function start($options = PTHREADS_INHERIT_ALL){ public function start($options = PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this); ThreadManager::getInstance()->add($this);
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
return parent::start($options); return parent::start($options);
} }
return false;
}
} }

View File

@ -26,9 +26,13 @@ namespace pocketmine;
*/ */
abstract class Worker extends \Worker{ abstract class Worker extends \Worker{
public final function start($options = PTHREADS_INHERIT_ALL){ public function start($options = PTHREADS_INHERIT_ALL){
ThreadManager::getInstance()->add($this); ThreadManager::getInstance()->add($this);
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated() and !$this->isShutdown()){
return parent::start($options); return parent::start($options);
} }
return false;
}
} }

View File

@ -34,7 +34,7 @@ class CommandReader extends Thread{
*/ */
public function __construct(\Threaded $threaded){ public function __construct(\Threaded $threaded){
$this->buffer = $threaded; $this->buffer = $threaded;
$this->start(); $this->start(PTHREADS_INHERIT_NONE);
} }
private function readLine(){ private function readLine(){

View File

@ -15,7 +15,8 @@ settings:
#Sends anonymous statistics to create usage reports #Sends anonymous statistics to create usage reports
send-usage: true send-usage: true
#Number of AsyncTask workers #Number of AsyncTask workers
async-workers: 4 #WARNING: This will increase global memory usage, but it won't be listed in the total.
async-workers: 1
debug: debug:
#If > 1, it will show debug messages in the console #If > 1, it will show debug messages in the console
@ -27,6 +28,7 @@ level-settings:
#The default format that levels will use when created #The default format that levels will use when created
default-format: mcregion default-format: mcregion
#If true, converts from a format that is not the default to the default format on load #If true, converts from a format that is not the default to the default format on load
#NOTE: This is currently not implemented
convert-format: false convert-format: false
chunk-sending: chunk-sending:
@ -42,6 +44,7 @@ chunk-ticking:
per-tick: 260 per-tick: 260
#Radius of chunks around a player to tick #Radius of chunks around a player to tick
tick-radius: 4 tick-radius: 4
#NOTE: This is currently not implemented
light-updates: false light-updates: false
clear-tick-list: false clear-tick-list: false

View File

@ -0,0 +1,109 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
*
*
*/
namespace pocketmine\scheduler;
use pocketmine\Server;
class AsyncPool{
/** @var Server */
private $server;
protected $size;
/** @var AsyncTask[] */
private $tasks = [];
/** @var int[] */
private $taskWorkers = [];
/** @var AsyncWorker[] */
private $workers = [];
/** @var int[] */
private $workerUsage = [];
public function __construct(Server $server, $size){
$this->server = $server;
$this->size = (int) $size;
for($i = 0; $i < $this->size; ++$i){
$this->workerUsage[$i] = 0;
$this->workers[$i] = new AsyncWorker();
$this->workers[$i]->start();
}
}
public function submitTask(AsyncTask $task){
if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){
return;
}
$this->tasks[$task->getTaskId()] = $task;
$selectedWorker = mt_rand(0, $this->size - 1);
$selectedTasks = $this->workerUsage[$selectedWorker];
for($i = 0; $i < $this->size; ++$i){
if($this->workerUsage[$i] < $selectedTasks){
$selectedWorker = $i;
$selectedTasks = $this->workerUsage[$i];
}
}
$this->workers[$selectedWorker]->stack($task);
$this->workerUsage[$selectedWorker]++;
$this->taskWorkers[$task->getTaskId()] = $selectedWorker;
}
private function removeTask(AsyncTask $task){
if(isset($this->taskWorkers[$task->getTaskId()])){
$this->workers[$this->taskWorkers[$task->getTaskId()]]->unstack($task);
}
unset($this->tasks[$task->getTaskId()]);
unset($this->taskWorkers[$task->getTaskId()]);
}
public function removeTasks(){
foreach($this->tasks as $task){
$this->removeTask($task);
}
for($i = 0; $i < $this->size; ++$i){
$this->workerUsage[$i] = 0;
}
$this->taskWorkers = [];
$this->tasks = [];
}
public function collectTasks(){
foreach($this->tasks as $task){
if($task->isGarbage()){
$task->onCompletion($this->server);
$this->workerUsage[$this->taskWorkers[$task->getTaskId()]]--;
unset($this->tasks[$task->getTaskId()]);
unset($this->taskWorkers[$task->getTaskId()]);
}
}
}
}

View File

@ -30,23 +30,25 @@ use pocketmine\Server;
*/ */
abstract class AsyncTask extends \Collectable{ abstract class AsyncTask extends \Collectable{
private $finished = null;
private $result = null; private $result = null;
/** @var int */
private $taskId = null; private $taskId = null;
public function run(){ public function run(){
$this->finished = false;
$this->result = null; $this->result = null;
$this->onRun(); $this->onRun();
$this->finished = true;
$this->setGarbage();
} }
/** /**
* @deprecated
*
* @return bool * @return bool
*/ */
public function isFinished(){ public function isFinished(){
return $this->finished === true; return $this->isGarbage();
} }
/** /**

View File

@ -34,6 +34,11 @@ class AsyncWorker extends Worker{
} }
$autoloader = new \BaseClassLoader(); $autoloader = new \BaseClassLoader();
$autoloader->addPath(\pocketmine\PATH . "src"); $autoloader->addPath(\pocketmine\PATH . "src");
$autoloader->addPath(\pocketmine\PATH . "src" . DIRECTORY_SEPARATOR . "spl");
$autoloader->register(true); $autoloader->register(true);
} }
public function start($options = PTHREADS_INHERIT_NONE){
parent::start(PTHREADS_INHERIT_CONSTANTS | PTHREADS_INHERIT_FUNCTIONS);
}
} }

View File

@ -37,9 +37,4 @@ class SendUsageTask extends AsyncTask{
public function onRun(){ public function onRun(){
Utils::postURL($this->endpoint, unserialize($this->data)); Utils::postURL($this->endpoint, unserialize($this->data));
} }
public function onCompletion(Server $server){
}
} }

View File

@ -42,14 +42,9 @@ class ServerScheduler{
*/ */
protected $tasks = []; protected $tasks = [];
/** @var \Pool */ /** @var AsyncPool */
protected $asyncPool; protected $asyncPool;
/** @var AsyncTask[] */
protected $asyncTaskStorage = [];
protected $asyncTasks = 0;
/** @var int */ /** @var int */
private $ids = 1; private $ids = 1;
@ -58,7 +53,7 @@ class ServerScheduler{
public function __construct(){ public function __construct(){
$this->queue = new ReversePriorityQueue(); $this->queue = new ReversePriorityQueue();
$this->asyncPool = new \Pool(self::$WORKERS, AsyncWorker::class); $this->asyncPool = new AsyncPool(Server::getInstance(), self::$WORKERS);
} }
/** /**
@ -80,9 +75,7 @@ class ServerScheduler{
public function scheduleAsyncTask(AsyncTask $task){ public function scheduleAsyncTask(AsyncTask $task){
$id = $this->nextId(); $id = $this->nextId();
$task->setTaskId($id); $task->setTaskId($id);
$this->asyncPool->submit($task); $this->asyncPool->submitTask($task);
$this->asyncTaskStorage[$task->getTaskId()] = $task;
++$this->asyncTasks;
} }
/** /**
@ -144,11 +137,9 @@ class ServerScheduler{
$task->cancel(); $task->cancel();
} }
$this->tasks = []; $this->tasks = [];
$this->asyncTaskStorage = []; $this->asyncPool->removeTasks();
//$this->asyncPool->shutdown();
$this->asyncTasks = 0;
$this->queue = new ReversePriorityQueue(); $this->queue = new ReversePriorityQueue();
$this->asyncPool = new \Pool(self::$WORKERS, AsyncWorker::class); $this->ids = 1;
} }
/** /**
@ -237,7 +228,8 @@ class ServerScheduler{
$task->run($this->currentTick); $task->run($this->currentTick);
}catch(\Exception $e){ }catch(\Exception $e){
Server::getInstance()->getLogger()->critical("Could not execute task " . $task->getTaskName() . ": " . $e->getMessage()); Server::getInstance()->getLogger()->critical("Could not execute task " . $task->getTaskName() . ": " . $e->getMessage());
if(($logger = Server::getInstance()->getLogger()) instanceof MainLogger){ $logger = Server::getInstance()->getLogger();
if($logger instanceof MainLogger){
$logger->logException($e); $logger->logException($e);
} }
} }
@ -252,26 +244,7 @@ class ServerScheduler{
} }
} }
if($this->asyncTasks > 0){ //Garbage collector $this->asyncPool->collectTasks();
$this->asyncPool->collect([$this, "collectAsyncTask"]);
if($this->asyncTasks > 0){
foreach($this->asyncTaskStorage as $asyncTask){
$this->collectAsyncTask($asyncTask);
}
}
}
}
public function collectAsyncTask(AsyncTask $task){
if($task->isFinished() and !$task->isGarbage()){
--$this->asyncTasks;
$task->onCompletion(Server::getInstance());
$task->setGarbage();
unset($this->asyncTaskStorage[$task->getTaskId()]);
}
return $task->isGarbage();
} }
private function isReady($currentTicks){ private function isReady($currentTicks){