AsyncTask: TLS now supports storing multiple values (now requires a key/value pair)

This commit is contained in:
Dylan K. Taylor 2019-04-18 18:58:31 +01:00
parent a4c7ec077b
commit 752e398970
9 changed files with 43 additions and 23 deletions

View File

@ -122,6 +122,8 @@ class TimingsCommand extends VanillaCommand{
$host = $sender->getServer()->getProperty("timings.host", "timings.pmmp.io"); $host = $sender->getServer()->getProperty("timings.host", "timings.pmmp.io");
$sender->getServer()->getAsyncPool()->submitTask(new class($sender, $host, $agent, $data) extends BulkCurlTask{ $sender->getServer()->getAsyncPool()->submitTask(new class($sender, $host, $agent, $data) extends BulkCurlTask{
private const TLS_KEY_SENDER = "sender";
/** @var string */ /** @var string */
private $host; private $host;
@ -139,11 +141,12 @@ class TimingsCommand extends VanillaCommand{
]] ]]
]); ]);
$this->host = $host; $this->host = $host;
$this->storeLocal($sender); $this->storeLocal(self::TLS_KEY_SENDER, $sender);
} }
public function onCompletion() : void{ public function onCompletion() : void{
$sender = $this->fetchLocal(); /** @var CommandSender $sender */
$sender = $this->fetchLocal(self::TLS_KEY_SENDER);
if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
return; return;
} }

View File

@ -29,6 +29,7 @@ use pocketmine\level\SimpleChunkManager;
use pocketmine\scheduler\AsyncTask; use pocketmine\scheduler\AsyncTask;
class PopulationTask extends AsyncTask{ class PopulationTask extends AsyncTask{
private const TLS_KEY_WORLD = "world";
public $state; public $state;
public $levelId; public $levelId;
@ -53,7 +54,7 @@ class PopulationTask extends AsyncTask{
$this->{"chunk$i"} = $c !== null ? $c->fastSerialize() : null; $this->{"chunk$i"} = $c !== null ? $c->fastSerialize() : null;
} }
$this->storeLocal($level); $this->storeLocal(self::TLS_KEY_WORLD, $level);
} }
public function onRun() : void{ public function onRun() : void{
@ -138,7 +139,7 @@ class PopulationTask extends AsyncTask{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var Level $level */ /** @var Level $level */
$level = $this->fetchLocal(); $level = $this->fetchLocal(self::TLS_KEY_WORLD);
if(!$level->isClosed()){ if(!$level->isClosed()){
if(!$this->state){ if(!$this->state){
$level->registerGeneratorToWorker($this->worker->getAsyncWorkerId()); $level->registerGeneratorToWorker($this->worker->getAsyncWorkerId());

View File

@ -29,11 +29,12 @@ use pocketmine\level\Level;
use pocketmine\scheduler\AsyncTask; use pocketmine\scheduler\AsyncTask;
class LightPopulationTask extends AsyncTask{ class LightPopulationTask extends AsyncTask{
private const TLS_KEY_WORLD = "world";
public $chunk; public $chunk;
public function __construct(Level $level, Chunk $chunk){ public function __construct(Level $level, Chunk $chunk){
$this->storeLocal($level); $this->storeLocal(self::TLS_KEY_WORLD, $level);
$this->chunk = $chunk->fastSerialize(); $this->chunk = $chunk->fastSerialize();
} }
@ -53,7 +54,7 @@ class LightPopulationTask extends AsyncTask{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var Level $level */ /** @var Level $level */
$level = $this->fetchLocal(); $level = $this->fetchLocal(self::TLS_KEY_WORLD);
if(!$level->isClosed()){ if(!$level->isClosed()){
/** @var Chunk $chunk */ /** @var Chunk $chunk */
$chunk = Chunk::fastDeserialize($this->chunk); $chunk = Chunk::fastDeserialize($this->chunk);

View File

@ -28,6 +28,9 @@ use pocketmine\network\mcpe\protocol\FullChunkDataPacket;
use pocketmine\scheduler\AsyncTask; use pocketmine\scheduler\AsyncTask;
class ChunkRequestTask extends AsyncTask{ class ChunkRequestTask extends AsyncTask{
private const TLS_KEY_PROMISE = "promise";
private const TLS_KEY_ERROR_HOOK = "errorHook";
/** @var string */ /** @var string */
protected $chunk; protected $chunk;
/** @var int */ /** @var int */
@ -44,7 +47,8 @@ class ChunkRequestTask extends AsyncTask{
$this->chunkX = $chunkX; $this->chunkX = $chunkX;
$this->chunkZ = $chunkZ; $this->chunkZ = $chunkZ;
$this->storeLocal(["promise" => $promise, "errorHook" => $onError]); $this->storeLocal(self::TLS_KEY_PROMISE, $promise);
$this->storeLocal(self::TLS_KEY_ERROR_HOOK, $onError);
} }
public function onRun() : void{ public function onRun() : void{
@ -60,7 +64,8 @@ class ChunkRequestTask extends AsyncTask{
} }
public function onError() : void{ public function onError() : void{
$hook = $this->fetchLocal()["errorHook"]; /** @var \Closure $hook */
$hook = $this->fetchLocal(self::TLS_KEY_ERROR_HOOK);
if($hook !== null){ if($hook !== null){
$hook(); $hook();
} }
@ -68,7 +73,7 @@ class ChunkRequestTask extends AsyncTask{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var CompressBatchPromise $promise */ /** @var CompressBatchPromise $promise */
$promise = $this->fetchLocal()["promise"]; $promise = $this->fetchLocal(self::TLS_KEY_PROMISE);
$promise->resolve($this->getResult()); $promise->resolve($this->getResult());
} }
} }

View File

@ -27,6 +27,8 @@ use pocketmine\scheduler\AsyncTask;
class CompressBatchTask extends AsyncTask{ class CompressBatchTask extends AsyncTask{
private const TLS_KEY_PROMISE = "promise";
private $level; private $level;
private $data; private $data;
@ -38,7 +40,7 @@ class CompressBatchTask extends AsyncTask{
public function __construct(string $data, int $compressionLevel, CompressBatchPromise $promise){ public function __construct(string $data, int $compressionLevel, CompressBatchPromise $promise){
$this->data = $data; $this->data = $data;
$this->level = $compressionLevel; $this->level = $compressionLevel;
$this->storeLocal($promise); $this->storeLocal(self::TLS_KEY_PROMISE, $promise);
} }
public function onRun() : void{ public function onRun() : void{
@ -47,7 +49,7 @@ class CompressBatchTask extends AsyncTask{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var CompressBatchPromise $promise */ /** @var CompressBatchPromise $promise */
$promise = $this->fetchLocal(); $promise = $this->fetchLocal(self::TLS_KEY_PROMISE);
$promise->resolve($this->getResult()); $promise->resolve($this->getResult());
} }
} }

View File

@ -58,6 +58,7 @@ use const OPENSSL_ALGO_SHA384;
use const STR_PAD_LEFT; use const STR_PAD_LEFT;
class ProcessLoginTask extends AsyncTask{ class ProcessLoginTask extends AsyncTask{
private const TLS_KEY_SESSION = "session";
public const MOJANG_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V"; public const MOJANG_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V";
@ -100,7 +101,7 @@ class ProcessLoginTask extends AsyncTask{
private $handshakeJwt = null; private $handshakeJwt = null;
public function __construct(NetworkSession $session, LoginPacket $packet, bool $authRequired, bool $useEncryption = true){ public function __construct(NetworkSession $session, LoginPacket $packet, bool $authRequired, bool $useEncryption = true){
$this->storeLocal($session); $this->storeLocal(self::TLS_KEY_SESSION, $session);
$this->packet = $packet; $this->packet = $packet;
$this->authRequired = $authRequired; $this->authRequired = $authRequired;
$this->useEncryption = $useEncryption; $this->useEncryption = $useEncryption;
@ -243,7 +244,7 @@ class ProcessLoginTask extends AsyncTask{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var NetworkSession $session */ /** @var NetworkSession $session */
$session = $this->fetchLocal(); $session = $this->fetchLocal(self::TLS_KEY_SESSION);
if(!$session->isConnected()){ if(!$session->isConnected()){
$this->worker->getLogger()->error("Player " . $session->getDisplayName() . " was disconnected before their login could be verified"); $this->worker->getLogger()->error("Player " . $session->getDisplayName() . " was disconnected before their login could be verified");
}elseif($session->setAuthenticationStatus($this->authenticated, $this->authRequired, $this->error)){ }elseif($session->setAuthenticationStatus($this->authenticated, $this->authRequired, $this->error)){

View File

@ -213,9 +213,10 @@ abstract class AsyncTask extends \Threaded{
* (E.g. a {@link \pocketmine\Level} object is no longer usable because it is unloaded while the AsyncTask is * (E.g. a {@link \pocketmine\Level} object is no longer usable because it is unloaded while the AsyncTask is
* executing, or even a plugin might be unloaded). * executing, or even a plugin might be unloaded).
* *
* @param mixed $complexData the data to store * @param string $key
* @param mixed $complexData the data to store
*/ */
protected function storeLocal($complexData) : void{ protected function storeLocal(string $key, $complexData) : void{
if(self::$threadLocalStorage === null){ if(self::$threadLocalStorage === null){
/* /*
* It's necessary to use an object (not array) here because pthreads is stupid. Non-default array statics * It's necessary to use an object (not array) here because pthreads is stupid. Non-default array statics
@ -225,7 +226,7 @@ abstract class AsyncTask extends \Threaded{
*/ */
self::$threadLocalStorage = new \ArrayObject(); self::$threadLocalStorage = new \ArrayObject();
} }
self::$threadLocalStorage[spl_object_id($this)] = $complexData; self::$threadLocalStorage[spl_object_id($this)][$key] = $complexData;
} }
/** /**
@ -234,16 +235,19 @@ abstract class AsyncTask extends \Threaded{
* If you used storeLocal(), you can use this on the same thread to fetch data stored. This should be used during * If you used storeLocal(), you can use this on the same thread to fetch data stored. This should be used during
* onProgressUpdate() and onCompletion() to fetch thread-local data stored on the parent thread. * onProgressUpdate() and onCompletion() to fetch thread-local data stored on the parent thread.
* *
* @param string $key
*
* @return mixed * @return mixed
* *
* @throws \InvalidArgumentException if no data were stored by this AsyncTask instance. * @throws \InvalidArgumentException if no data were stored by this AsyncTask instance.
*/ */
protected function fetchLocal(){ protected function fetchLocal(string $key){
if(self::$threadLocalStorage === null or !isset(self::$threadLocalStorage[spl_object_id($this)])){ $id = spl_object_id($this);
if(self::$threadLocalStorage === null or !isset(self::$threadLocalStorage[$id][$key])){
throw new \InvalidArgumentException("No matching thread-local data found on this thread"); throw new \InvalidArgumentException("No matching thread-local data found on this thread");
} }
return self::$threadLocalStorage[spl_object_id($this)]; return self::$threadLocalStorage[$id][$key];
} }
final public function __destruct(){ final public function __destruct(){

View File

@ -30,6 +30,7 @@ use function is_array;
use function json_decode; use function json_decode;
class UpdateCheckTask extends AsyncTask{ class UpdateCheckTask extends AsyncTask{
private const TLS_KEY_UPDATER = "updater";
/** @var string */ /** @var string */
private $endpoint; private $endpoint;
@ -39,7 +40,7 @@ class UpdateCheckTask extends AsyncTask{
private $error = "Unknown error"; private $error = "Unknown error";
public function __construct(AutoUpdater $updater, string $endpoint, string $channel){ public function __construct(AutoUpdater $updater, string $endpoint, string $channel){
$this->storeLocal($updater); $this->storeLocal(self::TLS_KEY_UPDATER, $updater);
$this->endpoint = $endpoint; $this->endpoint = $endpoint;
$this->channel = $channel; $this->channel = $channel;
} }
@ -74,7 +75,7 @@ class UpdateCheckTask extends AsyncTask{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var AutoUpdater $updater */ /** @var AutoUpdater $updater */
$updater = $this->fetchLocal(); $updater = $this->fetchLocal(self::TLS_KEY_UPDATER);
if($this->hasResult()){ if($this->hasResult()){
$updater->checkUpdateCallback($this->getResult()); $updater->checkUpdateCallback($this->getResult());
}else{ }else{

View File

@ -40,10 +40,12 @@ class AsyncTaskPublishProgressRaceTest extends Test{
//this test is racy, but it should fail often enough to be a pest if something is broken //this test is racy, but it should fail often enough to be a pest if something is broken
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{ $this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
private const TLS_KEY_TEST = "test";
private static $success = false; private static $success = false;
public function __construct(AsyncTaskPublishProgressRaceTest $t){ public function __construct(AsyncTaskPublishProgressRaceTest $t){
$this->storeLocal($t); $this->storeLocal(self::TLS_KEY_TEST, $t);
} }
public function onRun() : void{ public function onRun() : void{
@ -59,7 +61,7 @@ class AsyncTaskPublishProgressRaceTest extends Test{
public function onCompletion() : void{ public function onCompletion() : void{
/** @var AsyncTaskPublishProgressRaceTest $t */ /** @var AsyncTaskPublishProgressRaceTest $t */
$t = $this->fetchLocal(); $t = $this->fetchLocal(self::TLS_KEY_TEST);
$t->setResult(self::$success ? Test::RESULT_OK : Test::RESULT_FAILED); $t->setResult(self::$success ? Test::RESULT_OK : Test::RESULT_FAILED);
} }
}); });