8000 Use sorted array to store timers and remove cancelled ones · reactphp/event-loop@efd037c · GitHub
[go: up one dir, main page]

Skip to content

Commit efd037c

Browse files
committed
Use sorted array to store timers and remove cancelled ones
1 parent e1e0647 commit efd037c

File tree

1 file changed

+23
-45
lines changed

1 file changed

+23
-45
lines changed

src/Timer/Timers.php

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
namespace React\EventLoop\Timer;
44

55
use React\EventLoop\TimerInterface;
6-
use SplObjectStorage;
7-
use SplPriorityQueue;
86

97
/**
108
* A scheduler implementation that can hold multiple timer instances
@@ -17,14 +15,8 @@
1715
final class Timers
1816
{
1917
private $time;
20-
private $timers;
21-
private $scheduler;
22-
23-
public function __construct()
24-
{
25-
$this->timers = new SplObjectStorage();
26-
$this->scheduler = new SplPriorityQueue();
27-
}
18+
private $timers = array();
19+
private $schedule = array();
2820

2921
public function updateTime()
3022
{
@@ -38,36 +30,26 @@ public function getTime()
3830

3931
public function add(TimerInterface $timer)
4032
{
41-
$interval = $timer->getInterval();
42-
$scheduledAt = $interval + microtime(true);
43-
44-
$this->timers->attach($timer, $scheduledAt);
45-
$this->scheduler->insert($timer, -$scheduledAt);
33+
$id = spl_object_hash($timer);
34+
$this->timers[$id] = $timer;
35+
$this->schedule[$id] = $timer->getInterval() + microtime(true);
36+
asort($this->schedule);
4637
}
4738

4839
public function contains(TimerInterface $timer)
4940
{
50-
return $this->timers->contains($timer);
41+
return isset($this->timers[spl_oject_hash($timer)]);
5142
}
5243

5344
public function cancel(TimerInterface $timer)
5445
{
55-
$this->timers->detach($timer);
46+
$id = spl_object_hash($timer);
47+
unset($this->timers[$id], $this->schedule[$id]);
5648
}
5749

5850
public function getFirst()
5951
{
60-
while ($this->scheduler->count()) {
61-
$timer = $this->scheduler->top();
62-
63-
if ($this->timers->contains($timer)) {
64-
return $this->timers[$timer];
65-
}
66-
67-
$this->scheduler->extract();
68-
}
69-
70-
return null;
52+
return reset($this->schedule);
7153
}
7254

7355
public function isEmpty()
@@ -78,31 +60,27 @@ public function isEmpty()
7860
public function tick()
7961
{
8062
$time = $this->updateTime();
81-
$timers = $this->timers;
82-
$scheduler = $this->scheduler;
83-
84-
while (!$scheduler->isEmpty()) {
85-
$timer = $scheduler->top();
8663

87-
if (!isset($timers[$timer])) {
88-
$scheduler->extract();
89-
$timers->detach($timer);
90-
91-
continue;
64+
foreach ($this->schedule as $id => $scheduled) {
65+
// schedule is ordered, so loop until first timer that is not scheduled for execution now
66+
if ($scheduled >= $time) {
67+
break;
9268
}
9369

94-
if ($timers[$timer] >= $time) {
95-
break;
70+
// skip any timers that are removed while we process the current schedule
71+
if (!isset($this->schedule[$id]) || $this->schedule[$id] !== $scheduled) {
72+
continue;
9673
}
9774

98-
$scheduler->extract();
75+
$timer = $this->timers[$id];
9976
call_user_func($timer->getCallback(), $timer);
10077

101-
if ($timer->isPeriodic() && isset($timers[$timer])) {
102-
$timers[$timer] = $scheduledAt = $timer->getInterval() + $time;
103-
$scheduler->insert($timer, -$scheduledAt);
78+
// re-schedule if this is a periodic timer and it has not been cancelled explicitly already
79+
if ($timer->isPeriodic() && isset($this->timers[$id])) {
80+
$this->schedule[$id] = $timer->getInterval() + $time;
81+
asort($this->schedule);
10482
} else {
105-
$timers->detach($timer);
83+
unset($this->timers[$id], $this->schedule[$id]);
10684
}
10785
}
10886
}

0 commit comments

Comments
 (0)
0