diff --git a/src/Timer/Timers.php b/src/Timer/Timers.php index 17bbdac8..7944b4c1 100644 --- a/src/Timer/Timers.php +++ b/src/Timer/Timers.php @@ -3,8 +3,6 @@ namespace React\EventLoop\Timer; use React\EventLoop\TimerInterface; -use SplObjectStorage; -use SplPriorityQueue; /** * A scheduler implementation that can hold multiple timer instances @@ -17,14 +15,9 @@ final class Timers { private $time; - private $timers; - private $scheduler; - - public function __construct() - { - $this->timers = new SplObjectStorage(); - $this->scheduler = new SplPriorityQueue(); - } + private $timers = array(); + private $schedule = array(); + private $sorted = true; public function updateTime() { @@ -38,36 +31,32 @@ public function getTime() public function add(TimerInterface $timer) { - $interval = $timer->getInterval(); - $scheduledAt = $interval + microtime(true); - - $this->timers->attach($timer, $scheduledAt); - $this->scheduler->insert($timer, -$scheduledAt); + $id = spl_object_hash($timer); + $this->timers[$id] = $timer; + $this->schedule[$id] = $timer->getInterval() + microtime(true); + $this->sorted = false; } public function contains(TimerInterface $timer) { - return $this->timers->contains($timer); + return isset($this->timers[spl_oject_hash($timer)]); } public function cancel(TimerInterface $timer) { - $this->timers->detach($timer); + $id = spl_object_hash($timer); + unset($this->timers[$id], $this->schedule[$id]); } public function getFirst() { - while ($this->scheduler->count()) { - $timer = $this->scheduler->top(); - - if ($this->timers->contains($timer)) { - return $this->timers[$timer]; - } - - $this->scheduler->extract(); + // ensure timers are sorted to simply accessing next (first) one + if (!$this->sorted) { + $this->sorted = true; + asort($this->schedule); } - return null; + return reset($this->schedule); } public function isEmpty() @@ -77,32 +66,34 @@ public function isEmpty() public function tick() { - $time = $this->updateTime(); - $timers = $this->timers; - $scheduler = $this->scheduler; - - while (!$scheduler->isEmpty()) { - $timer = $scheduler->top(); + // ensure timers are sorted so we can execute in order + if (!$this->sorted) { + $this->sorted = true; + asort($this->schedule); + } - if (!isset($timers[$timer])) { - $scheduler->extract(); - $timers->detach($timer); + $time = $this->updateTime(); - continue; + foreach ($this->schedule as $id => $scheduled) { + // schedule is ordered, so loop until first timer that is not scheduled for execution now + if ($scheduled >= $time) { + break; } - if ($timers[$timer] >= $time) { - break; + // skip any timers that are removed while we process the current schedule + if (!isset($this->schedule[$id]) || $this->schedule[$id] !== $scheduled) { + continue; } - $scheduler->extract(); + $timer = $this->timers[$id]; call_user_func($timer->getCallback(), $timer); - if ($timer->isPeriodic() && isset($timers[$timer])) { - $timers[$timer] = $scheduledAt = $timer->getInterval() + $time; - $scheduler->insert($timer, -$scheduledAt); + // re-schedule if this is a periodic timer and it has not been cancelled explicitly already + if ($timer->isPeriodic() && isset($this->timers[$id])) { + $this->schedule[$id] = $timer->getInterval() + $time; + $this->sorted = false; } else { - $timers->detach($timer); + unset($this->timers[$id], $this->schedule[$id]); } } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: