2
2
3
3
namespace React \EventLoop ;
4
4
5
+ use React \EventLoop \Timer \Timers ;
6
+
5
7
class StreamSelectLoop implements LoopInterface
6
8
{
7
- private $ timeout ;
9
+ const QUANTUM_INTERVAL = 1000000 ;
8
10
11
+ private $ timers ;
12
+ private $ running = false ;
9
13
private $ readStreams = array ();
10
14
private $ readListeners = array ();
11
-
12
15
private $ writeStreams = array ();
13
16
private $ writeListeners = array ();
14
17
15
- private $ stopped = false ;
16
-
17
- // timeout = microseconds
18
- public function __construct ($ timeout = 1000000 )
18
+ public function __construct ()
19
19
{
20
- $ this ->timeout = $ timeout ;
20
+ $ this ->timers = new Timers () ;
21
21
}
22
22
23
23
public function addReadStream ($ stream , $ listener )
@@ -66,17 +66,61 @@ public function removeStream($stream)
66
66
$ this ->removeWriteStream ($ stream );
67
67
}
68
68
69
- public function tick ()
69
+ public function addTimer ($ interval , $ callback )
70
+ {
71
+ return $ this ->timers ->add ($ interval , $ callback );
72
+ }
73
+
74
+ public function addPeriodicTimer ($ interval , $ callback )
75
+ {
76
+ return $ this ->timers ->add ($ interval , $ callback , true );
77
+ }
78
+
79
+ public function cancelTimer ($ signature )
80
+ {
81
+ $ this ->timers ->cancel ($ signature );
82
+ }
83
+
84
+ protected function getNextEventTime ()
85
+ {
86
+ $ nextEvent = $ this ->timers ->getFirst ();
87
+
88
+ if ($ nextEvent === -1 ) {
89
+ return self ::QUANTUM_INTERVAL ;
90
+ }
91
+
92
+ $ currentTime = microtime (true );
93
+ if ($ nextEvent > $ currentTime ) {
94
+ return ($ nextEvent - $ currentTime ) * 1000000 ;
95
+ }
96
+
97
+ return 0 ;
98
+ }
99
+
100
+ protected function sleepOnPendingTimers ()
101
+ {
102
+ if ($ this ->timers ->isEmpty ()) {
103
+ $ this ->running = false ;
104
+ } else {
105
+ // We use usleep() instead of stream_select() to emulate timeouts
106
+ // since the latter fails when there are no streams registered for
107
+ // read / write events. Blame PHP for us needing this hack.
108
+ usleep ($ this ->getNextEventTime ());
109
+ }
110
+ }
111
+
112
+ protected function runStreamSelect ()
70
113
{
71
114
$ read = $ this ->readStreams ?: null ;
72
115
$ write = $ this ->writeStreams ?: null ;
73
- $ excepts = null ;
116
+ $ except = null ;
74
117
75
118
if (!$ read && !$ write ) {
76
- return false ;
119
+ $ this ->sleepOnPendingTimers ();
120
+ return ;
77
121
}
78
122
79
- if (stream_select ($ read , $ write , $ except , 0 , $ this ->timeout ) > 0 ) {
123
+ if (stream_select ($ read , $ write , $ except , 0 , $ this ->getNextEventTime () ) > 0 ) {
80
124
if ($ read ) {
81
125
foreach ($ read as $ stream ) {
82
126
$ listener = $ this ->readListeners [(int ) $ stream ];
@@ -99,16 +143,22 @@ public function tick()
99
143
}
100
144
}
101
145
}
146
+ }
147
+
148
+ public function tick ()
149
+ {
150
+ $ this ->timers ->run ();
151
+ $ this ->runStreamSelect ();
102
152
103
- return true ;
153
+ return $ this -> running ;
104
154
}
105
155
106
156
public function run ()
107
157
{
108
158
// @codeCoverageIgnoreStart
109
- $ this ->stopped = false ;
159
+ $ this ->running = true ;
110
160
111
- while ($ this ->tick () === true && ! $ this -> stopped ) {
161
+ while ($ this ->tick ()) {
112
162
// NOOP
113
163
}
114
164
// @codeCoverageIgnoreEnd
@@ -117,7 +167,7 @@ public function run()
117
167
public function stop ()
118
168
{
119
169
// @codeCoverageIgnoreStart
120
- $ this ->stopped = true ;
170
+ $ this ->running = false ;
121
171
// @codeCoverageIgnoreEnd
122
172
}
123
173
}
0 commit comments