33namespace React \EventLoop \Timer ;
44
55use React \EventLoop \TimerInterface ;
6- use SplObjectStorage ;
7- use SplPriorityQueue ;
86
97/**
108 * A scheduler implementation that can hold multiple timer instances
1715final 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