cannot call async event in sync context + remove Event dependency for AsyncEventDelegate

This commit is contained in:
ShockedPlot7560 2023-10-27 21:37:56 +02:00
parent dc85bba995
commit ed739cff4f
No known key found for this signature in database
GPG Key ID: D7539B420F1FA86E
4 changed files with 46 additions and 23 deletions

View File

@ -25,13 +25,17 @@ namespace pocketmine\event;
use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
use pocketmine\timings\Timings;
use pocketmine\utils\ObjectSet;
use function array_shift;
use function count;
final class AsyncEventDelegate extends Event{
final class AsyncEventDelegate{
/** @phpstan-var ObjectSet<Promise<null>> $promises */
private ObjectSet $promises;
/** @var array<class-string<AsyncEvent&Event>, int> $delegatesCall */
private static array $delegatesCallDepth = [];
private const MAX_EVENT_CALL_DEPTH = 50;
public function __construct(
private AsyncEvent&Event $event
@ -42,9 +46,27 @@ final class AsyncEventDelegate extends Event{
/**
* @phpstan-return Promise<null>
*/
public function callAsync() : Promise{
public function call() : Promise{
$this->promises->clear();
return $this->callDepth($this->callAsyncDepth(...));
if(!isset(self::$delegatesCallDepth[$class = $this->event::class])){
self::$delegatesCallDepth[$class] = 0;
}
if(self::$delegatesCallDepth[$class] >= self::MAX_EVENT_CALL_DEPTH){
//this exception will be caught by the parent event call if all else fails
throw new \RuntimeException("Recursive event call detected (reached max depth of " . self::MAX_EVENT_CALL_DEPTH . " calls)");
}
$timings = Timings::getAsyncEventTimings($this->event);
$timings->startTiming();
++self::$delegatesCallDepth[$class];
try{
return $this->callAsyncDepth();
}finally{
--self::$delegatesCallDepth[$class];
$timings->stopTiming();
}
}
/**

View File

@ -32,9 +32,6 @@ trait AsyncEventTrait {
* @phpstan-return Promise<null>
*/
final public function callAsync() : Promise{
if(!isset($this->delegate)){
$this->delegate = new AsyncEventDelegate($this);
}
return $this->delegate->callAsync();
return (new AsyncEventDelegate($this))->call();
}
}

View File

@ -47,32 +47,24 @@ abstract class Event{
* @throws \RuntimeException if event call recursion reaches the max depth limit
*/
public function call() : void{
$this->callDepth(function(){
$handlers = HandlerListManager::global()->getHandlersFor(static::class);
foreach($handlers as $registration){
$registration->callEvent($this);
}
});
}
/**
* @template T
* @phpstan-param \Closure() : T $closure
* @phpstan-return T
*/
final protected function callDepth(\Closure $closure) : mixed{
if(self::$eventCallDepth >= self::MAX_EVENT_CALL_DEPTH){
//this exception will be caught by the parent event call if all else fails
throw new \RuntimeException("Recursive event call detected (reached max depth of " . self::MAX_EVENT_CALL_DEPTH . " calls)");
}
if($this instanceof AsyncEvent){
throw new \InvalidArgumentException("Cannot call async event synchronously");
}
$timings = Timings::getEventTimings($this);
$timings->startTiming();
$handlers = HandlerListManager::global()->getHandlersFor(static::class);
++self::$eventCallDepth;
try{
return $closure();
foreach($handlers as $registration){
$registration->callEvent($this);
}
}finally{
--self::$eventCallDepth;
$timings->stopTiming();

View File

@ -25,6 +25,7 @@ namespace pocketmine\timings;
use pocketmine\block\tile\Tile;
use pocketmine\entity\Entity;
use pocketmine\event\AsyncEvent;
use pocketmine\event\Event;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\ServerboundPacket;
@ -113,6 +114,8 @@ abstract class Timings{
/** @var TimingsHandler[] */
private static array $events = [];
/** @var TimingsHandler[] */
private static array $asyncEvents = [];
/** @var TimingsHandler[][] */
private static array $eventHandlers = [];
@ -304,6 +307,15 @@ abstract class Timings{
return self::$events[$eventClass];
}
public static function getAsyncEventTimings(AsyncEvent&Event $event) : TimingsHandler{
$eventClass = get_class($event);
if(!isset(self::$asyncEvents[$eventClass])){
self::$asyncEvents[$eventClass] = new TimingsHandler(self::shortenCoreClassName($eventClass, "pocketmine\\event\\"), group: "Events");
}
return self::$asyncEvents[$eventClass];
}
/**
* @phpstan-template TEvent of Event
* @phpstan-param class-string<TEvent> $event