diff --git a/src/pocketmine/MemoryManager.php b/src/pocketmine/MemoryManager.php index 3fd444d7c..0bf70c333 100644 --- a/src/pocketmine/MemoryManager.php +++ b/src/pocketmine/MemoryManager.php @@ -54,6 +54,13 @@ class MemoryManager{ private $chunkCache; private $cacheTrigger; + /** @var \WeakRef[] */ + private $leakWatch = []; + + private $leakInfo = []; + + private $leakSeed = 0; + public function __construct(Server $server){ $this->server = $server; @@ -172,4 +179,85 @@ class MemoryManager{ return $cycles; } + + /** + * @param object $object + * + * @return string Object identifier for future checks + */ + public function addObjectWatcher($object){ + if(!is_object($object)){ + throw new \InvalidArgumentException("Not an object!"); + } + + + $identifier = spl_object_hash($object) . ":" . get_class($object); + + if(isset($this->leakInfo[$identifier])){ + return $this->leakInfo["id"]; + } + + $this->leakInfo[$identifier] = [ + "id" => $id = Utils::dataToUUID($identifier . ":" . $this->leakSeed++), + "class" => get_class($object), + "hash" => $identifier + ]; + $this->leakInfo[$id] = $this->leakInfo[$identifier]; + + $this->leakWatch[$id] = new \WeakRef($object); + + return $id; + } + + public function isObjectAlive($id){ + if(isset($this->leakWatch[$id])){ + return $this->leakWatch[$id]->valid(); + } + + return false; + } + + public function removeObjectWatch($id){ + if(!isset($this->leakWatch[$id])){ + return; + } + unset($this->leakInfo[$this->leakInfo[$id]["hash"]]); + unset($this->leakInfo[$id]); + unset($this->leakWatch[$id]); + } + + public function doObjectCleanup(){ + foreach($this->leakWatch as $id => $w){ + if(!$w->valid()){ + $this->removeObjectWatch($id); + } + } + } + + public function getObjectInformation($id, $includeObject = false){ + if(!isset($this->leakWatch[$id])){ + return null; + } + + $valid = false; + $references = 0; + $object = null; + + if($this->leakWatch[$id]->acquire()){ + $object = $this->leakWatch[$id]->get(); + $this->leakWatch[$id]->release(); + + $valid = true; + $references = getReferenceCount($object, false); + } + + return [ + "id" => $id, + "class" => $this->leakInfo[$id]["class"], + "hash" => $this->leakInfo[$id]["hash"], + "valid" => $valid, + "references" => $references, + "object" => $includeObject ? $object : null + ]; + } } \ No newline at end of file diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 129b60aec..7c6054d72 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -176,6 +176,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ */ public $loginData = []; + public $creationTime = 0; + protected $randomClientId; protected $uuid; @@ -499,6 +501,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); $this->uuid = Utils::dataToUUID($ip, $port, $clientID); + + $this->creationTime = microtime(true); } /** @@ -2703,7 +2707,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ unset($this->buffer); } - $this->perm->clearPermissions(); + if($this->perm !== null){ + $this->perm->clearPermissions(); + } + $this->server->removePlayer($this); } diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index d108f67f1..8ba486dd9 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -465,21 +465,8 @@ namespace pocketmine { $logger->info("Stopping other threads"); foreach(ThreadManager::getInstance()->getAll() as $id => $thread){ - if($thread->isRunning()){ - $logger->debug("Killing " . (new \ReflectionClass($thread))->getShortName() . " thread"); - $thread->kill(); - sleep(1); - $thread->detach(); - }elseif(!$thread->isJoined()){ - if(!$thread->isTerminated()){ - $logger->debug("Joining " . (new \ReflectionClass($thread))->getShortName() . " thread"); - $thread->join(); - }else{ - $logger->debug("Killing " . (new \ReflectionClass($thread))->getShortName() . " thread"); - $thread->kill(); - $thread->detach(); - } - } + $logger->debug("Stopping " . (new \ReflectionClass($thread))->getShortName() . " thread"); + $thread->quit(); } $logger->shutdown(); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index ad1bd5f6b..97f01bd55 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1570,10 +1570,10 @@ class Server{ if(($poolSize = $this->getProperty("settings.async-workers", "auto")) === "auto"){ $poolSize = ServerScheduler::$WORKERS; - $processors = Utils::getCoreCount(); + $processors = Utils::getCoreCount() - 2; if($processors > 0){ - $poolSize = max(2, $processors); + $poolSize = max(1, $processors); } } @@ -1957,8 +1957,10 @@ class Server{ $this->reloadWhitelist(); $this->operators->reload(); + $this->memoryManager->doObjectCleanup(); + foreach($this->getIPBans()->getEntries() as $entry){ - $this->blockAddress($entry->getName(), -1); + $this->getNetwork()->blockAddress($entry->getName(), -1); } $this->pluginManager->registerInterface(PharPluginLoader::class); @@ -2024,6 +2026,8 @@ class Server{ $interface->shutdown(); $this->network->unregisterInterface($interface); } + + $this->memoryManager->doObjectCleanup(); }catch(\Exception $e){ $this->logger->emergency("Crashed while crashing, killing process"); @kill(getmypid()); diff --git a/src/pocketmine/Thread.php b/src/pocketmine/Thread.php index d8f1ec5e4..a79385ad1 100644 --- a/src/pocketmine/Thread.php +++ b/src/pocketmine/Thread.php @@ -63,4 +63,25 @@ abstract class Thread extends \Thread{ return false; } + + /** + * Stops the thread using the best way possible. Try to stop it yourself before calling this. + */ + public function quit(){ + if($this->isRunning()){ + $this->kill(); + $this->detach(); + }elseif(!$this->isJoined()){ + if(!$this->isTerminated()){ + $this->join(); + }else{ + $this->kill(); + $this->detach(); + } + }else{ + $this->detach(); + } + + ThreadManager::getInstance()->remove($this); + } } \ No newline at end of file diff --git a/src/pocketmine/Worker.php b/src/pocketmine/Worker.php index 58ec885ec..4f8603ff9 100644 --- a/src/pocketmine/Worker.php +++ b/src/pocketmine/Worker.php @@ -63,4 +63,26 @@ abstract class Worker extends \Worker{ return false; } + + /** + * Stops the thread using the best way possible. Try to stop it yourself before calling this. + */ + public function quit(){ + if($this->isRunning()){ + $this->unstack(); + $this->kill(); + $this->detach(); + }elseif(!$this->isJoined()){ + if(!$this->isTerminated()){ + $this->join(); + }else{ + $this->kill(); + $this->detach(); + } + }else{ + $this->detach(); + } + + ThreadManager::getInstance()->remove($this); + } } \ No newline at end of file diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index ea105e62f..8918bd84d 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -82,7 +82,7 @@ abstract class Entity extends Location implements Metadatable{ const DATA_AIR = 1; const DATA_SHOW_NAMETAG = 3; const DATA_POTION_COLOR = 7; - const DATA_POTION_VISIBLE = 8; + const DATA_POTION_AMBIENT = 8; const DATA_NO_AI = 15; @@ -337,10 +337,10 @@ abstract class Entity extends Location implements Metadatable{ $b = ($color[2] / $count) & 0xff; $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, ($r << 16) + ($g << 8) + $b); - $this->setDataProperty(Entity::DATA_POTION_VISIBLE, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0); + $this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0); }else{ $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0); - $this->setDataProperty(Entity::DATA_POTION_VISIBLE, Entity::DATA_TYPE_BYTE, 0); + $this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, 0); } } diff --git a/src/pocketmine/network/RakLibInterface.php b/src/pocketmine/network/RakLibInterface.php index ff02619bb..000fb912d 100644 --- a/src/pocketmine/network/RakLibInterface.php +++ b/src/pocketmine/network/RakLibInterface.php @@ -98,9 +98,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ $this->identifiers->detach($player); unset($this->players[$identifier]); unset($this->identifiersACK[$identifier]); - if(!$player->closed){ - $player->close($player->getLeaveMessage(), $reason); - } + $player->close($player->getLeaveMessage(), $reason); } } diff --git a/src/pocketmine/plugin/PluginManager.php b/src/pocketmine/plugin/PluginManager.php index 168f93d0d..c01cc6aaf 100644 --- a/src/pocketmine/plugin/PluginManager.php +++ b/src/pocketmine/plugin/PluginManager.php @@ -459,8 +459,9 @@ class PluginManager{ $subs = []; foreach($this->permSubs[$permission] as $k => $perm){ /** @var \WeakRef $perm */ - if($perm->valid()){ + if($perm->acquire()){ $subs[] = $perm->get(); + $perm->release(); }else{ unset($this->permSubs[$permission][$k]); } @@ -507,8 +508,9 @@ class PluginManager{ if($op === true){ foreach($this->defSubsOp as $k => $perm){ /** @var \WeakRef $perm */ - if($perm->valid()){ + if($perm->acquire()){ $subs[] = $perm->get(); + $perm->release(); }else{ unset($this->defSubsOp[$k]); } @@ -516,8 +518,9 @@ class PluginManager{ }else{ foreach($this->defSubs as $k => $perm){ /** @var \WeakRef $perm */ - if($perm->valid()){ + if($perm->acquire()){ $subs[] = $perm->get(); + $perm->release(); }else{ unset($this->defSubs[$k]); } diff --git a/src/pocketmine/scheduler/AsyncPool.php b/src/pocketmine/scheduler/AsyncPool.php index ad7dcdabb..20b3e3216 100644 --- a/src/pocketmine/scheduler/AsyncPool.php +++ b/src/pocketmine/scheduler/AsyncPool.php @@ -117,8 +117,6 @@ class AsyncPool{ unset($this->taskWorkers[$task->getTaskId()]); $task->cleanObject(); - - unset($task); } public function removeTasks(){