mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-07 22:39:43 +00:00
AsyncEvent: make the code easier to make sense of
This commit is contained in:
parent
cb2fadeb26
commit
409066c8f5
@ -26,7 +26,6 @@ namespace pocketmine\event;
|
|||||||
use pocketmine\promise\Promise;
|
use pocketmine\promise\Promise;
|
||||||
use pocketmine\promise\PromiseResolver;
|
use pocketmine\promise\PromiseResolver;
|
||||||
use pocketmine\timings\Timings;
|
use pocketmine\timings\Timings;
|
||||||
use pocketmine\utils\ObjectSet;
|
|
||||||
use function array_shift;
|
use function array_shift;
|
||||||
use function count;
|
use function count;
|
||||||
|
|
||||||
@ -37,17 +36,14 @@ use function count;
|
|||||||
* When all the promises of a priority level have been resolved, the next priority level is called.
|
* When all the promises of a priority level have been resolved, the next priority level is called.
|
||||||
*/
|
*/
|
||||||
abstract class AsyncEvent{
|
abstract class AsyncEvent{
|
||||||
/** @phpstan-var ObjectSet<Promise<null>> $promises */
|
|
||||||
private ObjectSet $promises;
|
|
||||||
/** @var array<class-string<AsyncEvent>, int> $delegatesCallDepth */
|
/** @var array<class-string<AsyncEvent>, int> $delegatesCallDepth */
|
||||||
private static array $delegatesCallDepth = [];
|
private static array $delegatesCallDepth = [];
|
||||||
private const MAX_EVENT_CALL_DEPTH = 50;
|
private const MAX_EVENT_CALL_DEPTH = 50;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-return Promise<self>
|
* @phpstan-return Promise<static>
|
||||||
*/
|
*/
|
||||||
final public function call() : Promise{
|
final public function call() : Promise{
|
||||||
$this->promises = new ObjectSet();
|
|
||||||
if(!isset(self::$delegatesCallDepth[$class = static::class])){
|
if(!isset(self::$delegatesCallDepth[$class = static::class])){
|
||||||
self::$delegatesCallDepth[$class] = 0;
|
self::$delegatesCallDepth[$class] = 0;
|
||||||
}
|
}
|
||||||
@ -62,7 +58,12 @@ abstract class AsyncEvent{
|
|||||||
|
|
||||||
++self::$delegatesCallDepth[$class];
|
++self::$delegatesCallDepth[$class];
|
||||||
try{
|
try{
|
||||||
return $this->callAsyncDepth();
|
/** @phpstan-var PromiseResolver<static> $globalResolver */
|
||||||
|
$globalResolver = new PromiseResolver();
|
||||||
|
|
||||||
|
$this->asyncEachPriority(HandlerListManager::global()->getAsyncListFor(static::class), EventPriority::ALL, $globalResolver);
|
||||||
|
|
||||||
|
return $globalResolver->getPromise();
|
||||||
}finally{
|
}finally{
|
||||||
--self::$delegatesCallDepth[$class];
|
--self::$delegatesCallDepth[$class];
|
||||||
$timings->stopTiming();
|
$timings->stopTiming();
|
||||||
@ -70,81 +71,84 @@ abstract class AsyncEvent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-return Promise<self>
|
* TODO: this should use EventPriority constants for the list type but it's inconvenient with the current design
|
||||||
|
* @phpstan-param list<int> $remaining
|
||||||
|
* @phpstan-param PromiseResolver<static> $globalResolver
|
||||||
*/
|
*/
|
||||||
private function callAsyncDepth() : Promise{
|
private function asyncEachPriority(AsyncHandlerList $handlerList, array $remaining, PromiseResolver $globalResolver) : void{
|
||||||
/** @phpstan-var PromiseResolver<self> $globalResolver */
|
while(true){
|
||||||
$globalResolver = new PromiseResolver();
|
$nextPriority = array_shift($remaining);
|
||||||
|
if($nextPriority === null){
|
||||||
$handlerList = HandlerListManager::global()->getAsyncListFor(static::class);
|
|
||||||
$priorities = EventPriority::ALL;
|
|
||||||
$testResolve = function () use ($handlerList, &$testResolve, &$priorities, $globalResolver){
|
|
||||||
if(count($priorities) === 0){
|
|
||||||
$globalResolver->resolve($this);
|
$globalResolver->resolve($this);
|
||||||
}else{
|
break;
|
||||||
$this->callPriority($handlerList, array_shift($priorities))->onCompletion(function() use ($testResolve) : void{
|
|
||||||
$testResolve();
|
|
||||||
}, function () use ($globalResolver) {
|
|
||||||
$globalResolver->reject();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
$testResolve();
|
$promise = $this->callPriority($handlerList, $nextPriority);
|
||||||
|
if($promise !== null){
|
||||||
return $globalResolver->getPromise();
|
$promise->onCompletion(
|
||||||
|
onSuccess: fn() => $this->asyncEachPriority($handlerList, $remaining, $globalResolver),
|
||||||
|
onFailure: $globalResolver->reject(...)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-return Promise<null>
|
* @phpstan-return Promise<null>
|
||||||
*/
|
*/
|
||||||
private function callPriority(AsyncHandlerList $handlerList, int $priority) : Promise{
|
private function callPriority(AsyncHandlerList $handlerList, int $priority) : ?Promise{
|
||||||
$handlers = $handlerList->getListenersByPriority($priority);
|
$handlers = $handlerList->getListenersByPriority($priority);
|
||||||
|
if(count($handlers) === 0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @phpstan-var PromiseResolver<null> $resolver */
|
/** @phpstan-var PromiseResolver<null> $resolver */
|
||||||
$resolver = new PromiseResolver();
|
$resolver = new PromiseResolver();
|
||||||
|
|
||||||
|
$concurrentPromises = [];
|
||||||
$nonConcurrentHandlers = [];
|
$nonConcurrentHandlers = [];
|
||||||
foreach($handlers as $registration){
|
foreach($handlers as $registration){
|
||||||
if($registration->canBeCalledConcurrently()){
|
if($registration->canBeCalledConcurrently()){
|
||||||
$result = $registration->callAsync($this);
|
$result = $registration->callAsync($this);
|
||||||
if($result !== null) {
|
if($result !== null) {
|
||||||
$this->promises->add($result);
|
$concurrentPromises[] = $result;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$nonConcurrentHandlers[] = $registration;
|
$nonConcurrentHandlers[] = $registration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$testResolve = function() use (&$nonConcurrentHandlers, &$testResolve, $resolver){
|
Promise::all($concurrentPromises)->onCompletion(
|
||||||
$this->waitForPromises()->onCompletion(function() use (&$nonConcurrentHandlers, $testResolve, $resolver){
|
onSuccess: fn() => $this->processExclusiveHandlers($nonConcurrentHandlers, $resolver),
|
||||||
$handler = array_shift($nonConcurrentHandlers);
|
onFailure: $resolver->reject(...)
|
||||||
if($handler !== null){
|
);
|
||||||
$result = $handler->callAsync($this);
|
|
||||||
if($result !== null) {
|
|
||||||
$this->promises->add($result);
|
|
||||||
}
|
|
||||||
$testResolve();
|
|
||||||
}else{
|
|
||||||
$resolver->resolve(null);
|
|
||||||
}
|
|
||||||
}, function() use ($resolver) {
|
|
||||||
$resolver->reject();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$testResolve();
|
|
||||||
|
|
||||||
return $resolver->getPromise();
|
return $resolver->getPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-return Promise<array<int, null>>
|
* @param AsyncRegisteredListener[] $handlers
|
||||||
|
* @phpstan-param PromiseResolver<null> $resolver
|
||||||
*/
|
*/
|
||||||
private function waitForPromises() : Promise{
|
private function processExclusiveHandlers(array $handlers, PromiseResolver $resolver) : void{
|
||||||
$array = $this->promises->toArray();
|
while(true){
|
||||||
$this->promises->clear();
|
$handler = array_shift($handlers);
|
||||||
|
if($handler === null){
|
||||||
|
$resolver->resolve(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$result = $handler->callAsync($this);
|
||||||
|
if($result instanceof Promise){
|
||||||
|
//wait for this promise to resolve before calling the next handler
|
||||||
|
$result->onCompletion(
|
||||||
|
onSuccess: fn() => $this->processExclusiveHandlers($handlers, $resolver),
|
||||||
|
onFailure: $resolver->reject(...)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return Promise::all($array);
|
//this handler didn't return a promise - continue directly to the next one
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user