3
3
namespace React \EventLoop \Timer ;
4
4
5
5
use React \EventLoop \TimerInterface ;
6
- use SplObjectStorage ;
7
- use SplPriorityQueue ;
8
6
9
7
/**
10
8
* A scheduler implementation that can hold multiple timer instances
17
15
final class Timers
18
16
{
19
17
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 ();
28
20
29
21
public function updateTime ()
30
22
{
@@ -38,36 +30,26 @@ public function getTime()
38
30
39
31
public function add (TimerInterface $ timer )
40
32
{
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 );
46
37
}
47
38
48
39
public function contains (TimerInterface $ timer )
49
40
{
50
- return $ this ->timers -> contains ($ timer );
41
+ return isset ( $ this ->timers [ spl_oject_hash ($ timer)] );
51
42
}
52
43
53
44
public function cancel (TimerInterface $ timer )
54
45
{
55
- $ this ->timers ->detach ($ timer );
46
+ $ id = spl_object_hash ($ timer );
47
+ unset($ this ->timers [$ id ], $ this ->schedule [$ id ]);
56
48
}
57
49
58
50
public function getFirst ()
59
51
{
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 );
71
53
}
72
54
73
55
public function isEmpty ()
@@ -78,31 +60,27 @@ public function isEmpty()
78
60
public function tick ()
79
61
{
80
62
$ time = $ this ->updateTime ();
81
- $ timers = $ this ->timers ;
82
- $ scheduler = $ this ->scheduler ;
83
-
84
- while (!$ scheduler ->isEmpty ()) {
85
- $ timer = $ scheduler ->top ();
86
63
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 ;
92
68
}
93
69
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 ;
96
73
}
97
74
98
- $ scheduler -> extract () ;
75
+ $ timer = $ this -> timers [ $ id ] ;
99
76
call_user_func ($ timer ->getCallback (), $ timer );
100
77
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 );
104
82
} else {
105
- $ timers-> detach ( $ timer );
83
+ unset( $ this -> timers [ $ id ], $ this -> schedule [ $ id ] );
106
84
}
107
85
}
108
86
}
0 commit comments