AsyncTask: lazy-init progressUpdates (#5806)

this is only possible since pthreads 5.1 and pmmpthread

the performance cost of this one ThreadSafeArray allocation is 30% of the total cost of allocating an AsyncTask object on Windows, which is enormous.

In past versions we couldn't lazily initialize it, because the object might get destroyed before the main thread had a chance to dereference it, leading to a crash when collecting completed tasks. This is no longer an issue thanks to object rescue behaviour implemented in pthreads 5.1.
I think this is probably OK in terms of thread-safety, as only one thread writes the property.
This commit is contained in:
Dylan T 2023-06-11 14:45:44 +01:00 committed by GitHub
parent 1b35c352cc
commit df4a8d4788
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 12 additions and 7 deletions

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\scheduler;
use pmmp\thread\Thread as NativeThread;
use pmmp\thread\ThreadSafeArray;
use pocketmine\snooze\SleeperHandler;
use pocketmine\thread\log\ThreadSafeLogger;
use pocketmine\thread\ThreadSafeClassLoader;
@ -146,7 +145,6 @@ class AsyncPool{
throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once");
}
$task->progressUpdates = new ThreadSafeArray();
$task->setSubmitted();
$this->getWorker($worker)->submit($task);

View File

@ -68,8 +68,8 @@ abstract class AsyncTask extends Runnable{
*/
private static ?\ArrayObject $threadLocalStorage = null;
/** @phpstan-var ThreadSafeArray<int, string> */
public ThreadSafeArray $progressUpdates;
/** @phpstan-var ThreadSafeArray<int, string>|null */
private ?ThreadSafeArray $progressUpdates = null;
private ThreadSafe|string|int|bool|null|float $result = null;
private bool $cancelRun = false;
@ -163,15 +163,22 @@ abstract class AsyncTask extends Runnable{
* @param mixed $progress A value that can be safely serialize()'ed.
*/
public function publishProgress(mixed $progress) : void{
$this->progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable");
$progressUpdates = $this->progressUpdates;
if($progressUpdates === null){
$progressUpdates = $this->progressUpdates = new ThreadSafeArray();
}
$progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable");
}
/**
* @internal Only call from AsyncPool.php on the main thread
*/
public function checkProgressUpdates() : void{
while(($progress = $this->progressUpdates->shift()) !== null){
$this->onProgressUpdate(igbinary_unserialize($progress));
$progressUpdates = $this->progressUpdates;
if($progressUpdates !== null){
while(($progress = $progressUpdates->shift()) !== null){
$this->onProgressUpdate(igbinary_unserialize($progress));
}
}
}