mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-18 17:34:11 +00:00
This is no longer needed. Doing worker GC (via collect()) frees the allocated memory anyway, so this function is now redundant.
178 lines
4.6 KiB
PHP
178 lines
4.6 KiB
PHP
<?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
|
|
* @link http://www.pocketmine.net/
|
|
*
|
|
*
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace pocketmine\scheduler;
|
|
|
|
use pocketmine\event\Timings;
|
|
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, int $size){
|
|
$this->server = $server;
|
|
$this->size = $size;
|
|
|
|
$memoryLimit = (int) max(-1, (int) $this->server->getProperty("memory.async-worker-hard-limit", 1024));
|
|
|
|
for($i = 0; $i < $this->size; ++$i){
|
|
$this->workerUsage[$i] = 0;
|
|
$this->workers[$i] = new AsyncWorker($this->server->getLogger(), $i + 1, $memoryLimit);
|
|
$this->workers[$i]->setClassLoader($this->server->getLoader());
|
|
$this->workers[$i]->start();
|
|
}
|
|
}
|
|
|
|
public function getSize() : int{
|
|
return $this->size;
|
|
}
|
|
|
|
public function increaseSize(int $newSize){
|
|
if($newSize > $this->size){
|
|
|
|
$memoryLimit = (int) max(-1, (int) $this->server->getProperty("memory.async-worker-hard-limit", 1024));
|
|
|
|
for($i = $this->size; $i < $newSize; ++$i){
|
|
$this->workerUsage[$i] = 0;
|
|
$this->workers[$i] = new AsyncWorker($this->server->getLogger(), $i + 1, $memoryLimit);
|
|
$this->workers[$i]->setClassLoader($this->server->getLoader());
|
|
$this->workers[$i]->start();
|
|
}
|
|
$this->size = $newSize;
|
|
}
|
|
}
|
|
|
|
public function submitTaskToWorker(AsyncTask $task, int $worker){
|
|
if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){
|
|
return;
|
|
}
|
|
|
|
if($worker < 0 or $worker >= $this->size){
|
|
throw new \InvalidArgumentException("Invalid worker $worker");
|
|
}
|
|
|
|
$this->tasks[$task->getTaskId()] = $task;
|
|
|
|
$this->workers[$worker]->stack($task);
|
|
$this->workerUsage[$worker]++;
|
|
$this->taskWorkers[$task->getTaskId()] = $worker;
|
|
}
|
|
|
|
public function submitTask(AsyncTask $task) : int{
|
|
if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){
|
|
return -1;
|
|
}
|
|
|
|
$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->submitTaskToWorker($task, $selectedWorker);
|
|
return $selectedWorker;
|
|
}
|
|
|
|
private function removeTask(AsyncTask $task, bool $force = false){
|
|
if(isset($this->taskWorkers[$task->getTaskId()])){
|
|
if(!$force and ($task->isRunning() or !$task->isGarbage())){
|
|
return;
|
|
}
|
|
$this->workerUsage[$this->taskWorkers[$task->getTaskId()]]--;
|
|
}
|
|
|
|
unset($this->tasks[$task->getTaskId()]);
|
|
unset($this->taskWorkers[$task->getTaskId()]);
|
|
}
|
|
|
|
public function removeTasks(){
|
|
do{
|
|
foreach($this->tasks as $task){
|
|
$task->cancelRun();
|
|
$this->removeTask($task);
|
|
}
|
|
|
|
if(count($this->tasks) > 0){
|
|
Server::microSleep(25000);
|
|
}
|
|
}while(count($this->tasks) > 0);
|
|
|
|
for($i = 0; $i < $this->size; ++$i){
|
|
$this->workerUsage[$i] = 0;
|
|
}
|
|
|
|
$this->taskWorkers = [];
|
|
$this->tasks = [];
|
|
|
|
$this->collectWorkers();
|
|
}
|
|
|
|
private function collectWorkers(){
|
|
foreach($this->workers as $worker){
|
|
$worker->collect();
|
|
}
|
|
}
|
|
|
|
public function collectTasks(){
|
|
Timings::$schedulerAsyncTimer->startTiming();
|
|
|
|
foreach($this->tasks as $task){
|
|
if(!$task->isGarbage()){
|
|
$task->checkProgressUpdates($this->server);
|
|
}
|
|
if($task->isGarbage() and !$task->isRunning() and !$task->isCrashed()){
|
|
if(!$task->hasCancelledRun()){
|
|
$task->onCompletion($this->server);
|
|
$this->server->getScheduler()->removeLocalComplex($task);
|
|
}
|
|
|
|
$this->removeTask($task);
|
|
}elseif($task->isTerminated() or $task->isCrashed()){
|
|
$this->server->getLogger()->critical("Could not execute asynchronous task " . (new \ReflectionClass($task))->getShortName() . ": Task crashed");
|
|
$this->removeTask($task, true);
|
|
}
|
|
}
|
|
|
|
$this->collectWorkers();
|
|
|
|
Timings::$schedulerAsyncTimer->stopTiming();
|
|
}
|
|
}
|