World: fixed yet another edge case in drainPopulationRequestQueue() leading to assertion failure

really had to go fucking nuclear on it :(
This commit is contained in:
Dylan K. Taylor 2021-11-03 14:58:58 +00:00
parent ef82a2cd79
commit 1ebb206762
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D

View File

@ -263,6 +263,12 @@ class World implements ChunkManager{
* @phpstan-var \SplQueue<int> * @phpstan-var \SplQueue<int>
*/ */
private \SplQueue $chunkPopulationRequestQueue; private \SplQueue $chunkPopulationRequestQueue;
/**
* @var true[] chunkHash => dummy
* @phpstan-var array<int, true>
*/
private array $chunkPopulationRequestQueueIndex = [];
/** @var bool[] */ /** @var bool[] */
private $generatorRegisteredWorkers = []; private $generatorRegisteredWorkers = [];
@ -2733,12 +2739,19 @@ class World implements ChunkManager{
} }
} }
private function addChunkHashToPopulationRequestQueue(int $chunkHash) : void{
if(!isset($this->chunkPopulationRequestQueueIndex[$chunkHash])){
$this->chunkPopulationRequestQueue->enqueue($chunkHash);
$this->chunkPopulationRequestQueueIndex[$chunkHash] = true;
}
}
/** /**
* @phpstan-return Promise<Chunk> * @phpstan-return Promise<Chunk>
*/ */
private function enqueuePopulationRequest(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{ private function enqueuePopulationRequest(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
$chunkHash = World::chunkHash($chunkX, $chunkZ); $chunkHash = World::chunkHash($chunkX, $chunkZ);
$this->chunkPopulationRequestQueue->enqueue($chunkHash); $this->addChunkHashToPopulationRequestQueue($chunkHash);
$resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver(); $resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver();
if($associatedChunkLoader === null){ if($associatedChunkLoader === null){
$temporaryLoader = new class implements ChunkLoader{}; $temporaryLoader = new class implements ChunkLoader{};
@ -2753,17 +2766,9 @@ class World implements ChunkManager{
private function drainPopulationRequestQueue() : void{ private function drainPopulationRequestQueue() : void{
$failed = []; $failed = [];
$seen = [];
while(count($this->activeChunkPopulationTasks) < $this->maxConcurrentChunkPopulationTasks && !$this->chunkPopulationRequestQueue->isEmpty()){ while(count($this->activeChunkPopulationTasks) < $this->maxConcurrentChunkPopulationTasks && !$this->chunkPopulationRequestQueue->isEmpty()){
$nextChunkHash = $this->chunkPopulationRequestQueue->dequeue(); $nextChunkHash = $this->chunkPopulationRequestQueue->dequeue();
if(isset($seen[$nextChunkHash])){ unset($this->chunkPopulationRequestQueueIndex[$nextChunkHash]);
//We may have previously requested this, decided we didn't want it, and then decided we did want it
//again, all before the generation request got executed. In that case, the chunk hash may appear in the
//queue multiple times (it can't be quickly removed from the queue when the request is cancelled, so we
//leave it in the queue).
continue;
}
$seen[$nextChunkHash] = true;
World::getXZ($nextChunkHash, $nextChunkX, $nextChunkZ); World::getXZ($nextChunkHash, $nextChunkX, $nextChunkZ);
if(isset($this->chunkPopulationRequestMap[$nextChunkHash])){ if(isset($this->chunkPopulationRequestMap[$nextChunkHash])){
assert(!isset($this->activeChunkPopulationTasks[$nextChunkHash]), "Population for chunk $nextChunkX $nextChunkZ already running"); assert(!isset($this->activeChunkPopulationTasks[$nextChunkHash]), "Population for chunk $nextChunkX $nextChunkZ already running");
@ -2779,7 +2784,7 @@ class World implements ChunkManager{
//these requests failed even though they weren't rate limited; we can't directly re-add them to the back of the //these requests failed even though they weren't rate limited; we can't directly re-add them to the back of the
//queue because it would result in an infinite loop //queue because it would result in an infinite loop
foreach($failed as $hash){ foreach($failed as $hash){
$this->chunkPopulationRequestQueue->enqueue($hash); $this->addChunkHashToPopulationRequestQueue($hash);
} }
} }
@ -2978,7 +2983,7 @@ class World implements ChunkManager{
//request failed, stick it back on the queue //request failed, stick it back on the queue
//we didn't resolve the promise or touch it in any way, so any fake chunk loaders are still valid and //we didn't resolve the promise or touch it in any way, so any fake chunk loaders are still valid and
//don't need to be added a second time. //don't need to be added a second time.
$this->chunkPopulationRequestQueue->enqueue($index); $this->addChunkHashToPopulationRequestQueue($index);
} }
$this->drainPopulationRequestQueue(); $this->drainPopulationRequestQueue();