From f4ae58dda21e1505fdfaf9d30c1a11d2263c3e59 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Wed, 10 Sep 2014 15:11:56 +0200 Subject: [PATCH 01/11] Removed pthreads workarounds --- src/pocketmine/Thread.php | 2 +- src/pocketmine/Worker.php | 2 +- src/pocketmine/level/generator/GenerationThread.php | 2 +- src/pocketmine/plugin/PluginManager.php | 1 + src/pocketmine/scheduler/AsyncWorker.php | 8 ++++++-- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pocketmine/Thread.php b/src/pocketmine/Thread.php index 104add953..6a06a0b66 100644 --- a/src/pocketmine/Thread.php +++ b/src/pocketmine/Thread.php @@ -29,6 +29,6 @@ abstract class Thread extends \Thread{ public final function start($options = PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); - return parent::start($options & ~PTHREADS_INHERIT_CLASSES); + return parent::start($options); } } \ No newline at end of file diff --git a/src/pocketmine/Worker.php b/src/pocketmine/Worker.php index 78f9f3bd9..b6b8e890a 100644 --- a/src/pocketmine/Worker.php +++ b/src/pocketmine/Worker.php @@ -29,6 +29,6 @@ abstract class Worker extends \Worker{ public final function start($options = PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); - return parent::start($options & ~PTHREADS_INHERIT_CLASSES); + return parent::start($options); } } \ No newline at end of file diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php index d30f34b13..58a6de654 100644 --- a/src/pocketmine/level/generator/GenerationThread.php +++ b/src/pocketmine/level/generator/GenerationThread.php @@ -92,7 +92,7 @@ class GenerationThread extends Thread{ error_reporting(-1); //Load removed dependencies, can't use require_once() foreach($this->loadPaths as $name => $path){ - if(!class_exists($name, false) and !class_exists($name, false)){ + if(!class_exists($name, false) and !interface_exists($name, false)){ require($path); } } diff --git a/src/pocketmine/plugin/PluginManager.php b/src/pocketmine/plugin/PluginManager.php index 994f1e451..2fa15b231 100644 --- a/src/pocketmine/plugin/PluginManager.php +++ b/src/pocketmine/plugin/PluginManager.php @@ -433,6 +433,7 @@ class PluginManager{ */ public function unsubscribeFromPermission($permission, Permissible $permissible){ if(isset($this->permSubs[$permission])){ + $this->permSubs[$permission][spl_object_hash($permissible)]->release(); unset($this->permSubs[$permission][spl_object_hash($permissible)]); } } diff --git a/src/pocketmine/scheduler/AsyncWorker.php b/src/pocketmine/scheduler/AsyncWorker.php index 690599d01..7b8530f9d 100644 --- a/src/pocketmine/scheduler/AsyncWorker.php +++ b/src/pocketmine/scheduler/AsyncWorker.php @@ -26,8 +26,12 @@ use pocketmine\Worker; class AsyncWorker extends Worker{ public function run(){ - require(\pocketmine\PATH . "src/spl/ClassLoader.php"); - require(\pocketmine\PATH . "src/spl/BaseClassLoader.php"); + if(!interface_exists("ClassLoader", false)){ + require(\pocketmine\PATH . "src/spl/ClassLoader.php"); + } + if(!class_exists("BaseClassLoader", false)){ + require(\pocketmine\PATH . "src/spl/BaseClassLoader.php"); + } $autoloader = new \BaseClassLoader(); $autoloader->addPath(\pocketmine\PATH . "src"); $autoloader->register(true); From 665c275bb78b3d35bdc750a3d8cf4a22f627dca5 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Thu, 11 Sep 2014 17:18:54 +0200 Subject: [PATCH 02/11] New improved RakLib version with pthreads changes --- src/raklib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raklib b/src/raklib index 6fad38f76..13daa8c4a 160000 --- a/src/raklib +++ b/src/raklib @@ -1 +1 @@ -Subproject commit 6fad38f761d9ee204839eb557503645799b2ec99 +Subproject commit 13daa8c4afbf45a63c0ff33f42caf1bddef83ba8 From dbd1f3f96ee1d1674f2b4bc4da94b0d2d2a840f1 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Thu, 11 Sep 2014 18:05:01 +0200 Subject: [PATCH 03/11] Use pthreads interface on Chunk Generation THread, remove IPC sockets, improve performance --- .../generator/GenerationChunkManager.php | 48 +++++-- .../level/generator/GenerationManager.php | 129 ++++++++---------- .../generator/GenerationRequestManager.php | 36 ++--- .../level/generator/GenerationThread.php | 54 +++++--- 4 files changed, 141 insertions(+), 126 deletions(-) diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php index 61304a454..5c8e7898d 100644 --- a/src/pocketmine/level/generator/GenerationChunkManager.php +++ b/src/pocketmine/level/generator/GenerationChunkManager.php @@ -75,10 +75,15 @@ class GenerationChunkManager implements ChunkManager{ * @param $chunkZ * * @return FullChunk + * + * @throws \Exception */ public function getChunk($chunkX, $chunkZ){ $index = Level::chunkHash($chunkX, $chunkZ); $chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index]; + if($chunk === null){ + throw new \Exception("null chunk received"); + } $this->changes[$index] = $chunk; return $chunk; @@ -134,21 +139,36 @@ class GenerationChunkManager implements ChunkManager{ } public function isChunkGenerated($chunkX, $chunkZ){ - return $this->getChunk($chunkX, $chunkZ)->isGenerated(); + try{ + return $this->getChunk($chunkX, $chunkZ)->isGenerated(); + }catch(\Exception $e){ + return false; + } } public function isChunkPopulated($chunkX, $chunkZ){ - return $this->getChunk($chunkX, $chunkZ)->isPopulated(); + try{ + return $this->getChunk($chunkX, $chunkZ)->isPopulated(); + }catch(\Exception $e){ + return false; + } } public function setChunkGenerated($chunkX, $chunkZ){ $chunk = $this->getChunk($chunkX, $chunkZ); $chunk->setGenerated(true); + try{ + $chunk = $this->getChunk($chunkX, $chunkZ); + $chunk->setGenerated(true); + }catch(\Exception $e){} } public function setChunkPopulated($chunkX, $chunkZ){ - $chunk = $this->getChunk($chunkX, $chunkZ); - $chunk->setPopulated(true); + + try{ + $chunk = $this->getChunk($chunkX, $chunkZ); + $chunk->setPopulated(true); + }catch(\Exception $e){} } protected function requestChunk($chunkX, $chunkZ){ @@ -181,7 +201,11 @@ class GenerationChunkManager implements ChunkManager{ * @return int 0-255 */ public function getBlockIdAt($x, $y, $z){ - return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f); + try{ + return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f); + }catch(\Exception $e){ + return 0; + } } /** @@ -193,7 +217,9 @@ class GenerationChunkManager implements ChunkManager{ * @param int $id 0-255 */ public function setBlockIdAt($x, $y, $z, $id){ - $this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff); + try{ + $this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff); + }catch(\Exception $e){} } /** @@ -206,7 +232,11 @@ class GenerationChunkManager implements ChunkManager{ * @return int 0-15 */ public function getBlockDataAt($x, $y, $z){ - return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f); + try{ + return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f); + }catch(\Exception $e){ + return 0; + } } /** @@ -218,7 +248,9 @@ class GenerationChunkManager implements ChunkManager{ * @param int $data 0-15 */ public function setBlockDataAt($x, $y, $z, $data){ - $this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f); + try{ + $this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f); + }catch(\Exception $e){} } public function shutdown(){ diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php index 95278e790..273446137 100644 --- a/src/pocketmine/level/generator/GenerationManager.php +++ b/src/pocketmine/level/generator/GenerationManager.php @@ -89,8 +89,9 @@ class GenerationManager{ */ const PACKET_SHUTDOWN = 0xff; + /** @var GenerationThread */ + protected $thread; - protected $socket; /** @var \Logger */ protected $logger; /** @var \ClassLoader */ @@ -110,12 +111,12 @@ class GenerationManager{ protected $shutdown = false; /** - * @param resource $socket - * @param \Logger $logger - * @param \ClassLoader $loader + * @param GenerationThread $thread + * @param \Logger $logger + * @param \ClassLoader $loader */ - public function __construct($socket, \Logger $logger, \ClassLoader $loader){ - $this->socket = $socket; + public function __construct(GenerationThread $thread, \Logger $logger, \ClassLoader $loader){ + $this->thread = $thread; $this->logger = $logger; $this->loader = $loader; $chunkX = $chunkZ = null; @@ -204,7 +205,7 @@ class GenerationManager{ public function requestChunk($levelID, $chunkX, $chunkZ){ $this->needsChunk[$levelID] = [$chunkX, $chunkZ]; $binary = chr(self::PACKET_REQUEST_CHUNK) . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ); - @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); + $this->thread->pushThreadToMainPacket($binary); do{ $this->readPacket(); @@ -221,76 +222,60 @@ class GenerationManager{ public function sendChunk($levelID, FullChunk $chunk){ $binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); - @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); - } - - protected function socketRead($len){ - $buffer = ""; - while(strlen($buffer) < $len){ - $buffer .= @socket_read($this->socket, $len - strlen($buffer)); - } - - return $buffer; + $this->thread->pushThreadToMainPacket($binary); } protected function readPacket(){ - $len = $this->socketRead(4); - if(($len = Binary::readInt($len)) <= 0){ - $this->shutdown = true; - $this->getLogger()->critical("Generation Thread found a stream error, shutting down"); + if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){ + $pid = ord($packet{0}); + $offset = 1; + if($pid === self::PACKET_REQUEST_CHUNK){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $chunkX = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $chunkZ = Binary::readInt(substr($packet, $offset, 4)); + $this->enqueueChunk($levelID, $chunkX, $chunkZ); + }elseif($pid === self::PACKET_SEND_CHUNK){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $len = ord($packet{$offset++}); + /** @var FullChunk $class */ + $class = substr($packet, $offset, $len); + $offset += $len; + $chunk = $class::fromBinary(substr($packet, $offset)); + $this->receiveChunk($levelID, $chunk); + }elseif($pid === self::PACKET_OPEN_LEVEL){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $seed = Binary::readInt(substr($packet, $offset, 4)); + $offset += 4; + $len = Binary::readShort(substr($packet, $offset, 2)); + $offset += 2; + $class = substr($packet, $offset, $len); + $offset += $len; + $options = unserialize(substr($packet, $offset)); + $this->openLevel($levelID, $seed, $class, $options); + }elseif($pid === self::PACKET_CLOSE_LEVEL){ + $levelID = Binary::readInt(substr($packet, $offset, 4)); + $this->closeLevel($levelID); + }elseif($pid === self::PACKET_ADD_NAMESPACE){ + $len = Binary::readShort(substr($packet, $offset, 2)); + $offset += 2; + $namespace = substr($packet, $offset, $len); + $offset += $len; + $path = substr($packet, $offset); + $this->loader->addPath($path); + }elseif($pid === self::PACKET_SHUTDOWN){ + foreach($this->levels as $level){ + $level->shutdown(); + } + $this->levels = []; - return; - } - - $packet = $this->socketRead($len); - - $pid = ord($packet{0}); - $offset = 1; - if($pid === self::PACKET_REQUEST_CHUNK){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $chunkX = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $chunkZ = Binary::readInt(substr($packet, $offset, 4)); - $this->enqueueChunk($levelID, $chunkX, $chunkZ); - }elseif($pid === self::PACKET_SEND_CHUNK){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $len = ord($packet{$offset++}); - /** @var FullChunk $class */ - $class = substr($packet, $offset, $len); - $offset += $len; - $chunk = $class::fromBinary(substr($packet, $offset)); - $this->receiveChunk($levelID, $chunk); - }elseif($pid === self::PACKET_OPEN_LEVEL){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $seed = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $len = Binary::readShort(substr($packet, $offset, 2)); - $offset += 2; - $class = substr($packet, $offset, $len); - $offset += $len; - $options = unserialize(substr($packet, $offset)); - $this->openLevel($levelID, $seed, $class, $options); - }elseif($pid === self::PACKET_CLOSE_LEVEL){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $this->closeLevel($levelID); - }elseif($pid === self::PACKET_ADD_NAMESPACE){ - $len = Binary::readShort(substr($packet, $offset, 2)); - $offset += 2; - $namespace = substr($packet, $offset, $len); - $offset += $len; - $path = substr($packet, $offset); - $this->loader->addPath($path); - }elseif($pid === self::PACKET_SHUTDOWN){ - foreach($this->levels as $level){ - $level->shutdown(); + $this->shutdown = true; } - $this->levels = []; - - $this->shutdown = true; - socket_close($this->socket); + }elseif(count($this->thread->getInternalQueue()) === 0){ + $this->thread->wait(50000); } } diff --git a/src/pocketmine/level/generator/GenerationRequestManager.php b/src/pocketmine/level/generator/GenerationRequestManager.php index 913aac485..d7b0c25fc 100644 --- a/src/pocketmine/level/generator/GenerationRequestManager.php +++ b/src/pocketmine/level/generator/GenerationRequestManager.php @@ -28,7 +28,6 @@ use pocketmine\utils\Binary; class GenerationRequestManager{ - protected $socket; /** @var Server */ protected $server; /** @var GenerationThread */ @@ -40,7 +39,6 @@ class GenerationRequestManager{ public function __construct(Server $server){ $this->server = $server; $this->generationThread = new GenerationThread($server->getLogger(), $server->getLoader()); - $this->socket = $this->generationThread->getExternalSocket(); } /** @@ -52,7 +50,7 @@ class GenerationRequestManager{ $buffer = chr(GenerationManager::PACKET_OPEN_LEVEL) . Binary::writeInt($level->getID()) . Binary::writeInt($level->getSeed()) . Binary::writeShort(strlen($generator)) . $generator . serialize($options); - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); + $this->generationThread->pushMainToThreadPacket($buffer); } /** @@ -60,31 +58,22 @@ class GenerationRequestManager{ */ public function closeLevel(Level $level){ $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($level->getID()); - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); + $this->generationThread->pushMainToThreadPacket($buffer); } public function addNamespace($namespace, $path){ $buffer = chr(GenerationManager::PACKET_ADD_NAMESPACE) . Binary::writeShort(strlen($namespace)) . $namespace . $path; - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); - } - - protected function socketRead($len){ - $buffer = ""; - while(strlen($buffer) < $len){ - $buffer .= @socket_read($this->socket, $len - strlen($buffer)); - } - - return $buffer; + $this->generationThread->pushMainToThreadPacket($buffer); } protected function sendChunk($levelID, FullChunk $chunk){ - $binary = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); - @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); + $buffer = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); + $this->generationThread->pushMainToThreadPacket($buffer); } public function requestChunk(Level $level, $chunkX, $chunkZ){ $buffer = chr(GenerationManager::PACKET_REQUEST_CHUNK) . Binary::writeInt($level->getID()) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ); - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); + $this->generationThread->pushMainToThreadPacket($buffer); } protected function handleRequest($levelID, $chunkX, $chunkZ){ @@ -97,7 +86,7 @@ class GenerationRequestManager{ } }else{ $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID); - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); + $this->generationThread->pushMainToThreadPacket($buffer); } } @@ -106,17 +95,12 @@ class GenerationRequestManager{ $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); }else{ $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID); - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); + $this->generationThread->pushMainToThreadPacket($buffer); } } public function handlePackets(){ - if(($len = @socket_read($this->socket, 4)) !== false and $len !== ""){ - if(strlen($len) < 4){ - $len .= $this->socketRead(4 - strlen($len)); - } - - $packet = $this->socketRead(Binary::readInt($len)); + while(strlen($packet = $this->generationThread->readThreadToMainPacket()) > 0){ $pid = ord($packet{0}); $offset = 1; @@ -145,7 +129,7 @@ class GenerationRequestManager{ public function shutdown(){ $buffer = chr(GenerationManager::PACKET_SHUTDOWN); - @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); + $this->generationThread->pushMainToThreadPacket($buffer); $this->generationThread->join(); } diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php index 58a6de654..bd7991596 100644 --- a/src/pocketmine/level/generator/GenerationThread.php +++ b/src/pocketmine/level/generator/GenerationThread.php @@ -32,15 +32,40 @@ class GenerationThread extends Thread{ /** @var \ThreadedLogger */ protected $logger; - protected $externalSocket; - protected $internalSocket; + /** @var \Threaded */ + protected $externalQueue; + /** @var \Threaded */ + protected $internalQueue; - public function getExternalSocket(){ - return $this->externalSocket; + /** + * @return \Threaded + */ + public function getInternalQueue(){ + return $this->internalQueue; } - public function getInternalSocket(){ - return $this->internalSocket; + /** + * @return \Threaded + */ + public function getExternalQueue(){ + return $this->externalQueue; + } + + public function pushMainToThreadPacket($str){ + $this->internalQueue[] = $str; + $this->notify(); + } + + public function readMainToThreadPacket(){ + return $this->internalQueue->shift(); + } + + public function pushThreadToMainPacket($str){ + $this->externalQueue[] = $str; + } + + public function readThreadToMainPacket(){ + return $this->externalQueue->shift(); } /** @@ -57,19 +82,8 @@ class GenerationThread extends Thread{ $this->addDependency($loadPaths, new \ReflectionClass($this->loader)); $this->loadPaths = array_reverse($loadPaths); - $sockets = []; - if(!socket_create_pair((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? AF_INET : AF_UNIX), SOCK_STREAM, 0, $sockets)){ - throw new \Exception("Could not create IPC sockets. Reason: " . socket_strerror(socket_last_error())); - } - - $this->internalSocket = $sockets[0]; - socket_set_block($this->internalSocket); //IMPORTANT! - @socket_set_option($this->internalSocket, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2); - @socket_set_option($this->internalSocket, SOL_SOCKET, SO_RCVBUF, 1024 * 1024 * 2); - $this->externalSocket = $sockets[1]; - socket_set_nonblock($this->externalSocket); - @socket_set_option($this->externalSocket, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2); - @socket_set_option($this->externalSocket, SOL_SOCKET, SO_RCVBUF, 1024 * 1024 * 2); + $this->externalQueue = new \Threaded(); + $this->internalQueue = new \Threaded(); $this->start(); } @@ -98,6 +112,6 @@ class GenerationThread extends Thread{ } $this->loader->register(); - $generationManager = new GenerationManager($this->getInternalSocket(), $this->getLogger(), $this->loader); + $generationManager = new GenerationManager($this, $this->getLogger(), $this->loader); } } \ No newline at end of file From d991c324352513e9d18da300f1f5e791381e8f14 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Thu, 11 Sep 2014 19:10:25 +0200 Subject: [PATCH 04/11] Fixed Generator Thread as specified on krakjoe/pthreads#349 --- src/pocketmine/level/generator/GenerationManager.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php index 273446137..694d2b020 100644 --- a/src/pocketmine/level/generator/GenerationManager.php +++ b/src/pocketmine/level/generator/GenerationManager.php @@ -275,7 +275,10 @@ class GenerationManager{ $this->shutdown = true; } }elseif(count($this->thread->getInternalQueue()) === 0){ - $this->thread->wait(50000); + $this->thread->synchronized(function(){ + $this->thread->wait(50000); + }); + } } From b5deae7ba0f110cf2e5088e3140176c6b0434b2b Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Thu, 11 Sep 2014 19:18:14 +0200 Subject: [PATCH 05/11] Added synchronization to GenerationThread::pushMainToThreadPacket() --- src/pocketmine/level/generator/GenerationThread.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php index bd7991596..01255f2ef 100644 --- a/src/pocketmine/level/generator/GenerationThread.php +++ b/src/pocketmine/level/generator/GenerationThread.php @@ -53,7 +53,9 @@ class GenerationThread extends Thread{ public function pushMainToThreadPacket($str){ $this->internalQueue[] = $str; - $this->notify(); + $this->synchronized(function(){ + $this->notify(); + }); } public function readMainToThreadPacket(){ From 83eb9f778a3b8c0f8709ed4db296c9b22df8876a Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 12 Sep 2014 01:05:32 +0200 Subject: [PATCH 06/11] Level generators can be set in server.properties --- src/pocketmine/Server.php | 10 ++++---- .../generator/GenerationChunkManager.php | 13 +++++----- .../level/generator/GenerationManager.php | 24 +++++++++++-------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 2c1f1cdbd..49b77c6e2 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1061,12 +1061,8 @@ class Server{ if($generator !== null and class_exists($generator) and is_subclass_of($generator, "pocketmine\\level\\generator\\Generator")){ $generator = new $generator($options); }else{ - if(strtoupper($this->getLevelType()) == "FLAT"){ - $generator = Generator::getGenerator("flat"); - $options["preset"] = $this->getConfigString("generator-settings", ""); - }else{ - $generator = Generator::getGenerator("normal"); - } + $options["preset"] = $this->getConfigString("generator-settings", ""); + $generator = Generator::getGenerator($this->getLevelType()); } if(($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "mcregion"))) === null){ @@ -2051,6 +2047,8 @@ class Server{ foreach($this->interfaces as $interface){ $interface->process(); } + + Timings::$connectionTimer->stopTiming(); Timings::$schedulerTimer->startTiming(); diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php index 5c8e7898d..4a8f275ea 100644 --- a/src/pocketmine/level/generator/GenerationChunkManager.php +++ b/src/pocketmine/level/generator/GenerationChunkManager.php @@ -44,14 +44,13 @@ class GenerationChunkManager implements ChunkManager{ protected $changes = []; public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){ - if(!is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){ - throw new \Exception("Class is not a subclass of Generator"); + if(!class_exists($class, true) or !is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){ + throw new \Exception("Class $class does not exists or is not a subclass of Generator"); } $this->levelID = $levelID; $this->seed = $seed; $this->manager = $manager; - $this->generator = new $class($options); $this->generator->init($this, new Random($seed)); } @@ -116,9 +115,11 @@ class GenerationChunkManager implements ChunkManager{ } public function generateChunk($chunkX, $chunkZ){ - $this->getChunk($chunkX, $chunkZ); - $this->generator->generateChunk($chunkX, $chunkZ); - $this->setChunkGenerated($chunkX, $chunkZ); + try{ + $this->getChunk($chunkX, $chunkZ); + $this->generator->generateChunk($chunkX, $chunkZ); + $this->setChunkGenerated($chunkX, $chunkZ); + }catch(\Exception $e){} } public function populateChunk($chunkX, $chunkZ){ diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php index 694d2b020..9be9290cd 100644 --- a/src/pocketmine/level/generator/GenerationManager.php +++ b/src/pocketmine/level/generator/GenerationManager.php @@ -122,18 +122,22 @@ class GenerationManager{ $chunkX = $chunkZ = null; while($this->shutdown !== true){ - if(count($this->requestQueue) > 0){ - foreach($this->requestQueue as $levelID => $chunks){ - if(count($chunks) === 0){ - unset($this->requestQueue[$levelID]); - }else{ - Level::getXZ($key = key($chunks), $chunkX, $chunkZ); - unset($this->requestQueue[$levelID][$key]); - $this->generateChunk($levelID, $chunkX, $chunkZ); + try{ + if(count($this->requestQueue) > 0){ + foreach($this->requestQueue as $levelID => $chunks){ + if(count($chunks) === 0){ + unset($this->requestQueue[$levelID]); + }else{ + Level::getXZ($key = key($chunks), $chunkX, $chunkZ); + unset($this->requestQueue[$levelID][$key]); + $this->generateChunk($levelID, $chunkX, $chunkZ); + } } + }else{ + $this->readPacket(); } - }else{ - $this->readPacket(); + }catch(\Exception $e){ + $this->logger->warning("[Generator Thread] Exception: ".$e->getMessage() . " on file \"".$e->getFile()."\" line ".$e->getLine()); } } } From 315ea2ea3c388c3d530140fd232559e5880873b7 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 12 Sep 2014 12:58:21 +0200 Subject: [PATCH 07/11] 64-bit Random optimizations --- src/pocketmine/utils/Random.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pocketmine/utils/Random.php b/src/pocketmine/utils/Random.php index 924298e34..f47fb5c4e 100644 --- a/src/pocketmine/utils/Random.php +++ b/src/pocketmine/utils/Random.php @@ -46,7 +46,7 @@ class Random{ * @param int $seed Integer to be used as seed. */ public function setSeed($seed){ - $this->seed = crc32(Binary::writeInt($seed)); + $this->seed = crc32(pack("N", $seed)); } /** @@ -64,14 +64,14 @@ class Random{ * @return int */ public function nextSignedInt(){ - $t = crc32(Binary::writeInt($this->seed)); + $t = crc32(pack("N", $this->seed)); $this->seed ^= $t; - if($t > 2147483647){ - $t -= 4294967296; + if(PHP_INT_SIZE === 8){ + return $t << 32 >> 32; + }else{ + return $t; } - - return (int) $t; } /** @@ -105,11 +105,11 @@ class Random{ * Returns a random integer between $start and $end * * @param int $start default 0 - * @param int $end default PHP_INT_MAX + * @param int $end default 0x7fffffff * * @return int */ - public function nextRange($start = 0, $end = PHP_INT_MAX){ + public function nextRange($start = 0, $end = 0x7fffffff){ return $start + ($this->nextInt() % ($end + 1 - $start)); } From c71689a919239095d97c74369624c18010fc1ea4 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 12 Sep 2014 15:56:21 +0200 Subject: [PATCH 08/11] Optimized server sleep times --- src/pocketmine/Server.php | 119 ++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 68 deletions(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 49b77c6e2..499e0eafb 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -129,7 +129,6 @@ class Server{ private $nextTick = 0; private $tickAverage = [20,20,20,20,20]; private $useAverage = [20,20,20,20,20]; - private $inTick = false; /** @var \AttachableThreadedLogger */ private $logger; @@ -1840,7 +1839,7 @@ class Server{ $this->getScheduler()->scheduleRepeatingTask(new CallbackTask([$this, "checkTicks"]), 20 * 5); - $this->logger->info("Default game type: " . self::getGamemodeString($this->getGamemode())); //TODO: string name + $this->logger->info("Default game type: " . self::getGamemodeString($this->getGamemode())); $this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"'); @@ -1937,17 +1936,8 @@ class Server{ private function tickProcessor(){ $lastLoop = 0; while($this->isRunning){ - ++$lastLoop; - - if(($ticks = $this->tick()) !== true){ - if($lastLoop > 2 and $lastLoop < 16){ - usleep(1000); - }elseif($lastLoop < 128){ - usleep(2000); - }else{ - usleep(10000); - } - } + $this->tick(); + usleep((int) max(1, ($this->nextTick - microtime(true)) * 1000000)); } } @@ -2028,63 +2018,56 @@ class Server{ /** * Tries to execute a server tick */ - public function tick(){ - if($this->inTick === false){ - $tickTime = microtime(true); - if($tickTime < $this->nextTick){ - return false; - } - - Timings::$serverTickTimer->startTiming(); - - $this->inTick = true; //Fix race conditions - ++$this->tickCounter; - - $this->checkConsole(); - - //TODO: move this to tick - Timings::$connectionTimer->startTiming(); - foreach($this->interfaces as $interface){ - $interface->process(); - } - - - Timings::$connectionTimer->stopTiming(); - - Timings::$schedulerTimer->startTiming(); - $this->scheduler->mainThreadHeartbeat($this->tickCounter); - Timings::$schedulerTimer->stopTiming(); - $this->checkTickUpdates($this->tickCounter); - - if(($this->tickCounter & 0b1111) === 0){ - $this->titleTick(); - if(isset($this->queryHandler) and ($this->tickCounter & 0b111111111) === 0){ - $this->queryHandler->regenerateInfo(); - } - } - - $this->generationManager->handlePackets(); - - Timings::$serverTickTimer->stopTiming(); - - TimingsHandler::tick(); - - $now = microtime(true); - array_shift($this->tickAverage); - $this->tickAverage[] = min(20, 1 / max(0.001, $now - $tickTime)); - array_shift($this->useAverage); - $this->useAverage[] = min(1, ($now - $tickTime) / 0.05); - - if(($this->nextTick - $tickTime) < -1){ - $this->nextTick = $tickTime; - } - $this->nextTick += 0.05; - $this->inTick = false; - - return true; + private function tick(){ + $tickTime = microtime(true); + if($tickTime < $this->nextTick){ + return false; } - return false; + Timings::$serverTickTimer->startTiming(); + + ++$this->tickCounter; + + $this->checkConsole(); + + Timings::$connectionTimer->startTiming(); + foreach($this->interfaces as $interface){ + $interface->process(); + } + Timings::$connectionTimer->stopTiming(); + + Timings::$schedulerTimer->startTiming(); + $this->scheduler->mainThreadHeartbeat($this->tickCounter); + Timings::$schedulerTimer->stopTiming(); + + $this->checkTickUpdates($this->tickCounter); + + if(($this->tickCounter & 0b1111) === 0){ + $this->titleTick(); + if(isset($this->queryHandler) and ($this->tickCounter & 0b111111111) === 0){ + $this->queryHandler->regenerateInfo(); + } + } + + $this->generationManager->handlePackets(); + + Timings::$serverTickTimer->stopTiming(); + + TimingsHandler::tick(); + + $now = microtime(true); + array_shift($this->tickAverage); + $this->tickAverage[] = min(20, 1 / max(0.001, $now - $tickTime)); + array_shift($this->useAverage); + $this->useAverage[] = min(1, ($now - $tickTime) / 0.05); + + if(($this->nextTick - $tickTime) < -1){ + $this->nextTick = $tickTime; + } + $this->nextTick += 0.05; + $this->inTick = false; + + return true; } } From 63fc229c1217c6829047ad51e756d5d5c13f9505 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 12 Sep 2014 15:57:02 +0200 Subject: [PATCH 09/11] Enabled default PHP GC on generator thread --- src/pocketmine/level/generator/GenerationChunkManager.php | 2 -- src/pocketmine/level/generator/GenerationThread.php | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php index 4a8f275ea..2ff15c489 100644 --- a/src/pocketmine/level/generator/GenerationChunkManager.php +++ b/src/pocketmine/level/generator/GenerationChunkManager.php @@ -110,8 +110,6 @@ class GenerationChunkManager implements ChunkManager{ unset($this->changes[$index]); } } - - gc_collect_cycles(); } public function generateChunk($chunkX, $chunkZ){ diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php index 01255f2ef..f54a2a681 100644 --- a/src/pocketmine/level/generator/GenerationThread.php +++ b/src/pocketmine/level/generator/GenerationThread.php @@ -106,6 +106,7 @@ class GenerationThread extends Thread{ public function run(){ error_reporting(-1); + gc_enable(); //Load removed dependencies, can't use require_once() foreach($this->loadPaths as $name => $path){ if(!class_exists($name, false) and !interface_exists($name, false)){ From 702b2e539ccbae62fd20f07c56f0ba89f4052dab Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sat, 13 Sep 2014 12:01:11 +0200 Subject: [PATCH 10/11] Update RakLib --- src/raklib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raklib b/src/raklib index 13daa8c4a..c32adbd6f 160000 --- a/src/raklib +++ b/src/raklib @@ -1 +1 @@ -Subproject commit 13daa8c4afbf45a63c0ff33f42caf1bddef83ba8 +Subproject commit c32adbd6f3e06e4002fce6987651e421b784e997 From da715e48e2becc76b15bfce8eb68c8f3abad4240 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sat, 13 Sep 2014 12:07:04 +0200 Subject: [PATCH 11/11] Bumped pthreads version --- .travis.yml | 2 +- src/pocketmine/PocketMine.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 831707633..dfc454e2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ branches: before_script: - mkdir plugins - wget -O plugins/DevTools.phar https://github.com/PocketMine/DevTools/releases/download/v1.9.0/DevTools_v1.9.0.phar - - pecl install channel://pecl.php.net/pthreads-2.0.7 + - pecl install channel://pecl.php.net/pthreads-2.0.8 - pecl install channel://pecl.php.net/weakref-0.2.4 - echo | pecl install channel://pecl.php.net/yaml-1.1.1 diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 19573ffb3..d44e1ec93 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -279,8 +279,8 @@ namespace pocketmine { if(substr_count($pthreads_version, ".") < 2){ $pthreads_version = "0.$pthreads_version"; } - if(version_compare($pthreads_version, "2.0.4") < 0){ - $logger->critical("pthreads >= 2.0.4 is required, while you have $pthreads_version."); + if(version_compare($pthreads_version, "2.0.8") < 0){ + $logger->critical("pthreads >= 2.0.8 is required, while you have $pthreads_version."); ++$errors; }