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.
This commit is contained in:
Dylan K. Taylor 2018-05-30 14:11:11 +01:00
parent 132746aa3d
commit 51f43fb375
9 changed files with 61 additions and 118 deletions

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 = [];

View File

@ -1,50 +0,0 @@
<?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\plugin\Plugin;
/**
* Base class for plugin tasks. Allows the Server to delete them easily when needed
*/
abstract class PluginTask extends Task{
/** @var Plugin */
protected $owner;
/**
* @param Plugin $owner
*/
public function __construct(Plugin $owner){
$this->owner = $owner;
}
/**
* @return Plugin
*/
final public function getOwner() : Plugin{
return $this->owner;
}
}

View File

@ -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;
}
}

View File

@ -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<Task>
@ -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++;
}
}

View File

@ -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 . ")";

@ -1 +1 @@
Subproject commit c54e6732b489b4124073eb6714c4fc594faf9c80
Subproject commit 8906ba9831c19cc36d66bb90c43ee1f9d674b0b2