diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 00aadfa47..c15bc19c3 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -200,6 +200,8 @@ class Level implements ChunkManager, Metadatable{ private $chunkPopulationLock = []; /** @var int */ private $chunkPopulationQueueSize = 2; + /** @var bool[] */ + private $generatorRegisteredWorkers = []; /** @var bool */ private $autoSave = true; @@ -245,9 +247,6 @@ class Level implements ChunkManager, Metadatable{ /** @var bool */ private $closed = false; - /** @var \Closure */ - private $asyncPoolStartHook; - public static function chunkHash(int $x, int $z) : int{ return (($x & 0xFFFFFFFF) << 32) | ($z & 0xFFFFFFFF); } @@ -363,10 +362,6 @@ class Level implements ChunkManager, Metadatable{ $this->temporalPosition = new Position(0, 0, 0, $this); $this->temporalVector = new Vector3(0, 0, 0); $this->tickRate = 1; - - $this->server->getAsyncPool()->addWorkerStartHook($this->asyncPoolStartHook = function(int $worker) : void{ - $this->registerGeneratorToWorker($worker); - }); } public function getTickRate() : int{ @@ -382,15 +377,16 @@ class Level implements ChunkManager, Metadatable{ } public function registerGeneratorToWorker(int $worker) : void{ + $this->generatorRegisteredWorkers[$worker] = true; $this->server->getAsyncPool()->submitTaskToWorker(new GeneratorRegisterTask($this, $this->generator, $this->provider->getGeneratorOptions()), $worker); } public function unregisterGenerator(){ $pool = $this->server->getAsyncPool(); - $pool->removeWorkerStartHook($this->asyncPoolStartHook); - foreach($pool->getRunningWorkers() as $i){ + foreach($this->generatorRegisteredWorkers as $i => $bool){ $pool->submitTaskToWorker(new GeneratorUnregisterTask($this), $i); } + $this->generatorRegisteredWorkers = []; } public function getBlockMetadata() : BlockMetadataStore{ @@ -2955,7 +2951,11 @@ class Level implements ChunkManager, Metadatable{ } } $task = new PopulationTask($this, $chunk); - $this->server->getAsyncPool()->submitTask($task); + $workerId = $this->server->getAsyncPool()->selectWorker(); + if(!isset($this->generatorRegisteredWorkers[$workerId])){ + $this->registerGeneratorToWorker($workerId); + } + $this->server->getAsyncPool()->submitTaskToWorker($task, $workerId); } } diff --git a/src/pocketmine/scheduler/AsyncPool.php b/src/pocketmine/scheduler/AsyncPool.php index f833c4813..dbdb1de9d 100644 --- a/src/pocketmine/scheduler/AsyncPool.php +++ b/src/pocketmine/scheduler/AsyncPool.php @@ -168,18 +168,15 @@ class AsyncPool{ } /** - * Submits an AsyncTask to the worker with the least load. If all workers are busy and the pool is not full, a new - * worker may be started. + * Selects a worker ID to run a task. * - * @param AsyncTask $task + * - if an idle worker is found, it will be selected + * - else, if the worker pool is not full, a new worker will be selected + * - else, the worker with the smallest backlog is chosen. * * @return int */ - public function submitTask(AsyncTask $task) : int{ - if($task->getTaskId() !== null){ - throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once"); - } - + public function selectWorker() : int{ $worker = null; $minUsage = PHP_INT_MAX; foreach($this->workerUsage as $i => $usage){ @@ -202,7 +199,23 @@ class AsyncPool{ } assert($worker !== null); + return $worker; + } + /** + * Submits an AsyncTask to the worker with the least load. If all workers are busy and the pool is not full, a new + * worker may be started. + * + * @param AsyncTask $task + * + * @return int + */ + public function submitTask(AsyncTask $task) : int{ + if($task->getTaskId() !== null){ + throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once"); + } + + $worker = $this->selectWorker(); $this->submitTaskToWorker($task, $worker); return $worker; }