From 51f43fb375057dc18d75130b6c3071a12550587d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 30 May 2018 14:11:11 +0100 Subject: [PATCH] Removed global ServerScheduler - plugins now get their own isolated schedulers This change breaks pretty much all API pertaining to synchronous task scheduling. Significant changes: - Server->getScheduler() has been removed - Plugin->getScheduler() has been added - every plugin now has its own scheduler - Because schedulers are now per-plugin, it is now unnecessary for PluginTask to exist because stopping plugin tasks on plugin disable is as simple as destroying the plugin's scheduler. Therefore PluginTask has now been removed and it is expected for things to now use the base Task class instead. For the most part, plugins will simply need to change Plugin->getServer()->getScheduler()->... to Plugin->getScheduler()->... Another highlight is that plugin tasks now no longer have global IDs - they are unique to each scheduler. --- src/pocketmine/Server.php | 18 +------ src/pocketmine/plugin/Plugin.php | 6 +++ src/pocketmine/plugin/PluginBase.php | 11 ++++ src/pocketmine/plugin/PluginManager.php | 10 +++- src/pocketmine/scheduler/PluginTask.php | 50 ------------------- src/pocketmine/scheduler/TaskHandler.php | 30 ++++++----- ...{ServerScheduler.php => TaskScheduler.php} | 36 ++++++------- src/pocketmine/timings/Timings.php | 16 +----- tests/plugins/PocketMine-TesterPlugin | 2 +- 9 files changed, 61 insertions(+), 118 deletions(-) delete mode 100644 src/pocketmine/scheduler/PluginTask.php rename src/pocketmine/scheduler/{ServerScheduler.php => TaskScheduler.php} (85%) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 92d5a1f46..56a6de309 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -90,7 +90,6 @@ use pocketmine\resourcepacks\ResourcePackManager; use pocketmine\scheduler\AsyncPool; use pocketmine\scheduler\FileWriteTask; use pocketmine\scheduler\SendUsageTask; -use pocketmine\scheduler\ServerScheduler; use pocketmine\snooze\SleeperHandler; use pocketmine\snooze\SleeperNotifier; use pocketmine\tile\Tile; @@ -149,8 +148,6 @@ class Server{ /** @var AutoUpdater */ private $updater = null; - /** @var ServerScheduler */ - private $scheduler = null; /** @var AsyncPool */ private $asyncPool; @@ -653,13 +650,6 @@ class Server{ return $this->resourceManager; } - /** - * @return ServerScheduler - */ - public function getScheduler(){ - return $this->scheduler; - } - public function getAsyncPool() : AsyncPool{ return $this->asyncPool; } @@ -1525,8 +1515,6 @@ class Server{ $this->logger->info($this->getLanguage()->translateString("pocketmine.server.start", [TextFormat::AQUA . $this->getVersion() . TextFormat::RESET])); - $this->scheduler = new ServerScheduler($this->logger); - if(($poolSize = $this->getProperty("settings.async-workers", "auto")) === "auto"){ $poolSize = 2; $processors = Utils::getCoreCount() - 2; @@ -2075,10 +2063,6 @@ class Server{ $this->getLogger()->debug("Removing event handlers"); HandlerList::unregisterAll(); - if($this->scheduler instanceof ServerScheduler){ - $this->getLogger()->debug("Stopping all tasks"); - $this->scheduler->cancelAllTasks(); - } if($this->asyncPool instanceof AsyncPool){ $this->getLogger()->debug("Shutting down async task worker pool"); $this->asyncPool->shutdown(); @@ -2534,7 +2518,7 @@ class Server{ Timings::$connectionTimer->stopTiming(); Timings::$schedulerTimer->startTiming(); - $this->scheduler->mainThreadHeartbeat($this->tickCounter); + $this->pluginManager->tickSchedulers($this->tickCounter); Timings::$schedulerTimer->stopTiming(); Timings::$schedulerAsyncTimer->startTiming(); diff --git a/src/pocketmine/plugin/Plugin.php b/src/pocketmine/plugin/Plugin.php index ecb008ac7..867b9a046 100644 --- a/src/pocketmine/plugin/Plugin.php +++ b/src/pocketmine/plugin/Plugin.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace pocketmine\plugin; use pocketmine\command\CommandExecutor; +use pocketmine\scheduler\TaskScheduler; use pocketmine\Server; use pocketmine\utils\Config; @@ -136,4 +137,9 @@ interface Plugin extends CommandExecutor{ */ public function getPluginLoader(); + /** + * @return TaskScheduler + */ + public function getScheduler() : TaskScheduler; + } diff --git a/src/pocketmine/plugin/PluginBase.php b/src/pocketmine/plugin/PluginBase.php index 6f7b4f6fe..de66a85d2 100644 --- a/src/pocketmine/plugin/PluginBase.php +++ b/src/pocketmine/plugin/PluginBase.php @@ -26,6 +26,7 @@ namespace pocketmine\plugin; use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\command\PluginIdentifiableCommand; +use pocketmine\scheduler\TaskScheduler; use pocketmine\Server; use pocketmine\utils\Config; @@ -58,6 +59,9 @@ abstract class PluginBase implements Plugin{ /** @var PluginLogger */ private $logger; + /** @var TaskScheduler */ + private $scheduler; + /** * Called when the plugin is loaded, before calling onEnable() */ @@ -119,6 +123,7 @@ abstract class PluginBase implements Plugin{ $this->file = rtrim($file, "\\/") . "/"; $this->configFile = $this->dataFolder . "config.yml"; $this->logger = new PluginLogger($this); + $this->scheduler = new TaskScheduler($this->logger); } } @@ -305,4 +310,10 @@ abstract class PluginBase implements Plugin{ return $this->loader; } + /** + * @return TaskScheduler + */ + public function getScheduler() : TaskScheduler{ + return $this->scheduler; + } } diff --git a/src/pocketmine/plugin/PluginManager.php b/src/pocketmine/plugin/PluginManager.php index 6ea2dc603..f7dfb1d49 100644 --- a/src/pocketmine/plugin/PluginManager.php +++ b/src/pocketmine/plugin/PluginManager.php @@ -657,7 +657,7 @@ class PluginManager{ $this->server->getLogger()->logException($e); } - $this->server->getScheduler()->cancelTasks($plugin); + $plugin->getScheduler()->shutdown(); HandlerList::unregisterAll($plugin); foreach($plugin->getDescription()->getPermissions() as $perm){ $this->removePermission($perm); @@ -665,6 +665,14 @@ class PluginManager{ } } + public function tickSchedulers(int $currentTick) : void{ + foreach($this->plugins as $p){ + if($p->isEnabled()){ + $p->getScheduler()->mainThreadHeartbeat($currentTick); + } + } + } + public function clearPlugins(){ $this->disablePlugins(); $this->plugins = []; diff --git a/src/pocketmine/scheduler/PluginTask.php b/src/pocketmine/scheduler/PluginTask.php deleted file mode 100644 index c5d503ec4..000000000 --- a/src/pocketmine/scheduler/PluginTask.php +++ /dev/null @@ -1,50 +0,0 @@ -owner = $owner; - } - - /** - * @return Plugin - */ - final public function getOwner() : Plugin{ - return $this->owner; - } - -} diff --git a/src/pocketmine/scheduler/TaskHandler.php b/src/pocketmine/scheduler/TaskHandler.php index 0e980b96f..6af785230 100644 --- a/src/pocketmine/scheduler/TaskHandler.php +++ b/src/pocketmine/scheduler/TaskHandler.php @@ -50,22 +50,26 @@ class TaskHandler{ /** @var TimingsHandler */ public $timings; - public $timingName = null; + /** @var string */ + private $taskName; + /** @var string */ + private $ownerName; /** - * @param string $timingName - * @param Task $task - * @param int $taskId - * @param int $delay - * @param int $period + * @param Task $task + * @param int $taskId + * @param int $delay + * @param int $period + * @param string|null $ownerName */ - public function __construct(string $timingName, Task $task, int $taskId, int $delay = -1, int $period = -1){ + public function __construct(Task $task, int $taskId, int $delay = -1, int $period = -1, ?string $ownerName = null){ $this->task = $task; $this->taskId = $taskId; $this->delay = $delay; $this->period = $period; - $this->timingName = $timingName ?? "Unknown"; - $this->timings = Timings::getPluginTaskTimings($this, $period); + $this->taskName = get_class($task); + $this->ownerName = $ownerName ?? "Unknown"; + $this->timings = Timings::getScheduledTaskTimings($this, $period); $this->task->setHandler($this); } @@ -164,10 +168,10 @@ class TaskHandler{ * @return string */ public function getTaskName() : string{ - if($this->timingName !== null){ - return $this->timingName; - } + return $this->taskName; + } - return get_class($this->task); + public function getOwnerName() : string{ + return $this->ownerName; } } diff --git a/src/pocketmine/scheduler/ServerScheduler.php b/src/pocketmine/scheduler/TaskScheduler.php similarity index 85% rename from src/pocketmine/scheduler/ServerScheduler.php rename to src/pocketmine/scheduler/TaskScheduler.php index 99b28c4d0..f24d900c6 100644 --- a/src/pocketmine/scheduler/ServerScheduler.php +++ b/src/pocketmine/scheduler/TaskScheduler.php @@ -27,13 +27,16 @@ declare(strict_types=1); namespace pocketmine\scheduler; -use pocketmine\plugin\Plugin; -use pocketmine\plugin\PluginException; use pocketmine\utils\ReversePriorityQueue; -class ServerScheduler{ +class TaskScheduler{ /** @var \Logger */ private $logger; + /** @var string|null */ + private $owner; + + /** @var bool */ + private $shutdown = false; /** * @var ReversePriorityQueue @@ -51,8 +54,10 @@ class ServerScheduler{ /** @var int */ protected $currentTick = 0; - public function __construct(\Logger $logger){ + + public function __construct(\Logger $logger, ?string $owner = null){ $this->logger = $logger; + $this->owner = $owner; $this->queue = new ReversePriorityQueue(); } @@ -106,19 +111,6 @@ class ServerScheduler{ } } - /** - * @param Plugin $plugin - */ - public function cancelTasks(Plugin $plugin){ - foreach($this->tasks as $taskId => $task){ - $ptask = $task->getTask(); - if($ptask instanceof PluginTask and $ptask->getOwner() === $plugin){ - $task->cancel(); - unset($this->tasks[$taskId]); - } - } - } - public function cancelAllTasks(){ foreach($this->tasks as $task){ $task->cancel(); @@ -146,11 +138,11 @@ class ServerScheduler{ * * @return null|TaskHandler * - * @throws PluginException + * @throws \InvalidStateException */ private function addTask(Task $task, int $delay, int $period){ - if($task instanceof PluginTask and !$task->getOwner()->isEnabled()){ - throw new PluginException("Plugin '" . $task->getOwner()->getName() . "' attempted to register a task while disabled"); + if($this->shutdown){ + throw new \InvalidStateException("Tried to schedule task after scheduler shutdown"); } if($delay <= 0){ @@ -163,7 +155,7 @@ class ServerScheduler{ $period = 1; } - return $this->handle(new TaskHandler(get_class($task), $task, $this->nextId(), $delay, $period)); + return $this->handle(new TaskHandler($task, $this->nextId(), $delay, $period, $this->owner)); } private function handle(TaskHandler $handler) : TaskHandler{ @@ -181,6 +173,7 @@ class ServerScheduler{ } public function shutdown() : void{ + $this->shutdown = true; $this->cancelAllTasks(); } @@ -225,5 +218,4 @@ class ServerScheduler{ private function nextId() : int{ return $this->ids++; } - } diff --git a/src/pocketmine/timings/Timings.php b/src/pocketmine/timings/Timings.php index cc92c6839..3e9145fab 100644 --- a/src/pocketmine/timings/Timings.php +++ b/src/pocketmine/timings/Timings.php @@ -27,7 +27,6 @@ use pocketmine\entity\Entity; use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\Player; use pocketmine\plugin\PluginManager; -use pocketmine\scheduler\PluginTask; use pocketmine\scheduler\TaskHandler; use pocketmine\tile\Tile; @@ -149,19 +148,8 @@ abstract class Timings{ * * @return TimingsHandler */ - public static function getPluginTaskTimings(TaskHandler $task, int $period) : TimingsHandler{ - $ftask = $task->getTask(); - if($ftask instanceof PluginTask and $ftask->getOwner() !== null){ - $plugin = $ftask->getOwner()->getDescription()->getFullName(); - }elseif($task->timingName !== null){ - $plugin = "Scheduler"; - }else{ - $plugin = "Unknown"; - } - - $taskname = $task->getTaskName(); - - $name = "Task: " . $plugin . " Runnable: " . $taskname; + public static function getScheduledTaskTimings(TaskHandler $task, int $period) : TimingsHandler{ + $name = "Task: " . ($task->getOwnerName() ?? "Unknown") . " Runnable: " . $task->getTaskName(); if($period > 0){ $name .= "(interval:" . $period . ")"; diff --git a/tests/plugins/PocketMine-TesterPlugin b/tests/plugins/PocketMine-TesterPlugin index c54e6732b..8906ba983 160000 --- a/tests/plugins/PocketMine-TesterPlugin +++ b/tests/plugins/PocketMine-TesterPlugin @@ -1 +1 @@ -Subproject commit c54e6732b489b4124073eb6714c4fc594faf9c80 +Subproject commit 8906ba9831c19cc36d66bb90c43ee1f9d674b0b2