10000 Merge remote-tracking branch 'nrk/loop-timers-abstraction' into 0.3 · Undefined-Variables/event-loop@ce86485 · GitHub
[go: up one dir, main page]

Skip to content

Commit ce86485

Browse files
committed
Merge remote-tracking branch 'nrk/loop-timers-abstraction' into 0.3
* nrk/loop-timers-abstraction: [EventLoop] Apply changes after code review. [Dns] Update after BC breaks in EventLoop timers. Update examples after BC breaks in EventLoop timers. [EventLoop] Add a method to check the status of a timer. [EventLoop] Update tests. [EventLoop] Add typehint in loop interface for timer cancelling. [EventLoop] Reimplement timers for LibEvLoop. [EventLoop] Reimplement timers for LibEventLoop. [EventLoop] Reimplement timers for StreamSelectLoop. [EventLoop] Add a timer interface and class.
2 parents 141a77d + cf697fe commit ce86485

File tree

7 files changed

+218
-104
lines changed

7 files changed

+218
-104
lines changed

LibEvLoop.php

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22

33
namespace React\EventLoop;
44

5+
use SplObjectStorage;
6+
use React\EventLoop\Timer\Timer;
7+
use React\EventLoop\Timer\TimerInterface;
8+
59
/**
610
* @see https://github.com/m4rw3r/php-libev
711
* @see https://gist.github.com/1688204
812
*/
913
class LibEvLoop implements LoopInterface
1014
{
1115
private $loop;
16+
private $timers;
1217
private $readEvents = array();
1318
private $writeEvents = array();
14-
private $timers = array();
1519

1620
public function __construct()
1721
{
1822
$this->loop = new \libev\EventLoop();
23+
$this->timers = new SplObjectStorage();
1924
}
2025

2126
public function addReadStream($stream, $listener)
@@ -85,49 +90,56 @@ private function wrapStreamListener($stream, $listener, $flags)
8590

8691
public function addTimer($interval, $callback)
8792
{
88-
$dummyCallback = function () {};
89-
$timer = new \libev\TimerEvent($dummyCallback, $interval);
93+
$timer = new Timer($this, $interval, $callback, false);
94+
$this->setupTimer($timer);
9095

91-
return $this->createTimer($timer, $callback, false);
96+
return $timer;
9297
}
9398

9499
public function addPeriodicTimer($interval, $callback)
95100
{
96-
$dummyCallback = function () {};
97-
$timer = new \libev\TimerEvent($dummyCallback, $interval, $interval);
101+
$timer = new Timer($this, $interval, $callback, true);
102+
$this->setupTimer($timer);
98103

99-
return $this->createTimer($timer, $callback, true);
104+
return $timer;
100105
}
101106

102-
public function cancelTimer($signature)
107+
public function cancelTimer(TimerInterface $timer)
103108
{
104-
$this->loop->remove($this->timers[$signature]);
105-
unset($this->timers[$signature]);
109+
if (isset($this->timers[$timer])) {
110+
$this->loop->remove($this->timers[$timer]);
111+
$this->timers->detach($timer);
112+
}
106113
}
107114

108-
private function createTimer($timer, $callback, $periodic)
115+
private function setupTimer(TimerInterface $timer)
109116
{
110-
$signature = spl_object_hash($timer);
111-
$callback = $this->wrapTimerCallback($signature, $callback, $periodic);
112-
$timer->setCallback($callback);
117+
$dummyCallback = function () {};
118+
$interval = $timer->getInterval();
113119

114-
$this->timers[$signature] = $timer;
115-
$this->loop->add($timer);
120+
if ($timer->isPeriodic()) {
121+
$libevTimer = new \libev\TimerEvent($dummyCallback, $interval, $interval);
122+
} else {
123+
$libevTimer = new \libev\TimerEvent($dummyCallback, $interval);
124+
}
116125

117-
return $signature;
118-
}
126+
$libevTimer->setCallback(function () use ($timer) {
127+
call_user_func($timer->getCallback(), $timer);
119128

120-
private function wrapTimerCallback($signature, $callback, $periodic)
121-
{
122-
$loop = $this;
129+
if (!$timer->isPeriodic()) {
130+
$timer->cancel();
131+
}
132+
});
123133

124-
return function ($event) use ($signature, $callback, $periodic, $loop) {
125-
call_user_func($callback, $signature, $loop);
134+
$this->timers->attach($timer, $libevTimer);
135+
$this->loop->add($libevTimer);
126136

127-
if (!$periodic) {
128-
$loop->cancelTimer($signature);
129-
}
130-
};
137+
return $timer;
138+
}
139+
140+
public function isTimerActive(TimerInterface $timer)
141+
{
142+
return $this->timers->contains($timer);
131143
}
132144

133145
public function tick()

LibEventLoop.php

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
namespace React\EventLoop;
44

5+
use SplObjectStorage;
6+
use React\EventLoop\Timer\Timer;
7+
use React\EventLoop\Timer\TimerInterface;
8+
59
class LibEventLoop implements LoopInterface
610
{
711
const MIN_TIMER_RESOLUTION = 0.001;
812

913
private $base;
1014
private $callback;
11-
12-
private $timers = array();
15+
private $timers;
1316

1417
private $events = array();
1518
private $flags = array();
@@ -20,6 +23,7 @@ public function __construct()
2023
{
2124
$this->base = event_base_new();
2225
$this->callback = $this->createLibeventCallback();
26+
$this->timers = new SplObjectStorage();
2327
}
2428

2529
protected function createLibeventCallback()
17AE
@@ -150,30 +154,20 @@ protected function addTimerInternal($interval, $callback, $periodic = false)
150154
throw new \InvalidArgumentException('Timer events do not support sub-millisecond timeouts.');
151155
}
152156

153-
if (!is_callable($callback)) {
154-
throw new \InvalidArgumentException('The callback must be a callable object.');
155-
}
156-
157-
$timer = (object) array(
158-
'loop' => $this,
159-
'resource' => $resource = event_new(),
160-
'callback' => $callback,
161-
'interval' => $interval * 1000000,
162-
'periodic' => $periodic,
163-
'cancelled' => false,
164-
);
157+
$timer = new Timer($this, $interval, $callback, $periodic);
158+
$resource = event_new();
165159

166-
$timer->signature = spl_object_hash($timer);
167-
$that = $this;
160+
$timers = $this->timers;
161+
$timers->attach($timer, $resource);
168162

169-
$callback = function () use ($timer, $that, &$callback) {
170-
if ($timer->cancelled === false) {
171-
call_user_func($timer->callback, $timer->signature, $timer->loop);
163+
$callback = function () use ($timers, $timer, &$callback) {
164+
if (isset($timers[$timer])) {
165+
call_user_func($timer->getCallback(), $timer);
172166

173-
if ($timer->periodic === true && $timer->cancelled === false) {
174-
event_add($timer->resource, $timer->interval);
167+
if ($timer->isPeriodic() && isset($timers[$timer])) {
168+
event_add($timers[$timer], $timer->getInterval() * 1000000);
175169
} else {
176-
$that->cancelTimer($timer->signature);
170+
$timer->cancel();
177171
}
178172
}
179173
};
@@ -182,9 +176,7 @@ protected function addTimerInternal($interval, $callback, $periodic = false)
182176
event_base_set($resource, $this->base);
183177
event_add($resource, $interval * 1000000);
184178

185-
$this->timers[$timer->signature] = $timer;
186-
187-
return $timer->signature;
179+
return $timer;
188180
}
189181

190182
public function addTimer($interval, $callback)
@@ -197,18 +189,22 @@ public function addPeriodicTimer($interval, $callback)
197189
return $this->addTimerInternal($interval, $callback, true);
198190
}
199191

200-
public function cancelTimer($signature)
192+
public function cancelTimer(TimerInterface $timer)
201193
{
202-
if (isset($this->timers[$signature])) {
203-
$timer = $this->timers[$signature];
194+
if (isset($this->timers[$timer])) {
195+
$resource = $this->timers[$timer];
196+
event_del($resource);
197+
event_free($resource);
204198

205-
$timer->cancelled = true;
206-
event_del($timer->resource);
207-
event_free($timer->resource);
208-
unset($this->timers[$signature]);
199+
$this->timers->detach($timer);
209200
}
210201
}
211202

203+
public function isTimerActive(TimerInterface $timer)
204+
{
205+
return $this->timers->contains($timer);
206+
}
207+
212208
public function tick()
213209
{
214210
event_base_loop($this->base, EVLOOP_ONCE | EVLOOP_NONBLOCK);

LoopInterface.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace React\EventLoop;
44

5+
use React\EventLoop\Timer\TimerInterface;
6+
57
interface LoopInterface
68
{
79
public function addReadStream($stream, $listener);
@@ -13,7 +15,8 @@ public function removeStream($stream);
1315

1416
public function addTimer($interval, $callback);
1517
public function addPeriodicTimer($interval, $callback);
16-
public function cancelTimer($signature);
18+
public function cancelTimer(TimerInterface $timer);
19+
public function isTimerActive(TimerInterface $timer);
1720

1821
public function tick();
1922
public function run();

StreamSelectLoop.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace React\EventLoop;
44

5+
use React\EventLoop\Timer\Timer;
6+
use React\EventLoop\Timer\TimerInterface;
57
use React\EventLoop\Timer\Timers;
68

79
class StreamSelectLoop implements LoopInterface
@@ -17,7 +19,7 @@ class StreamSelectLoop implements LoopInterface
1719

1820
public function __construct()
1921
{
20-
$this->timers = new Timers($this);
22+
$this->timers = new Timers();
2123
}
2224

2325
public function addReadStream($stream, $listener)
@@ -68,17 +70,28 @@ public function removeStream($stream)
6870

6971
public function addTimer($interval, $callback)
7072
{
71-
return $this->timers->add($interval, $callback);
73+
$timer = new Timer($this, $interval, $callback, false);
74+
$this->timers->add($timer);
75+
76+
return $timer;
7277
}
7378

7479
public function addPeriodicTimer($interval, $callback)
7580
{
76-
return $this->timers->add($interval, $callback, true);
81+
$timer = new Timer($this, $interval, $callback, true);
82+
$this->timers->add($timer);
83+
84+
return $timer;
85+
}
86+
87+
public function cancelTimer(TimerInterface $timer)
88+
{
89+
$this->timers->cancel($timer);
7790
}
7891

79-
public function cancelTimer($signature)
92+
public function isTimerActive(TimerInterface $timer)
8093
{
81-
$this->timers->cancel($signature);
94+
return $this->timers->contains($timer);
8295
}
8396

8497
protected function getNextEventTimeInMicroSeconds()

Timer/Timer.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace React\EventLoop\Timer;
4+
5+
use InvalidArgumentException;
6+
use React\EventLoop\LoopInterface;
7+
8+
class Timer implements TimerInterface
9+
{
10+
protected $loop;
11+
protected $interval;
12+
protected $callback;
13+
protected $periodic;
14+
protected $data;
15+
16+
public function __construct(LoopInterface $loop, $interval, $callback, $periodic = false, $data = null)
17+
{
18+
if (false === is_callable($callback)) {
19+
throw new InvalidArgumentException('The callback argument must be a valid callable object');
20+
}
21+
22+
$this->loop = $loop;
23+
$this->interval = (float) $interval;
24+
$this->callback = $callback;
25+
$this->periodic = (bool) $periodic;
26+
$this->data = null;
27+
}
28+
29+
public function getLoop()
30+
{
31+
return $this->loop;
32+
}
33+
34+
public function getInterval()
35+
{
36+
return $this->interval;
37+
}
38+
39+
public function getCallback()
40+
{
41+
return $this->callback;
42+
}
43+
44+
public function setData($data)
45+
{
46+
$this->data = $data;
47+
}
48+
49+
public function getData()
50+
{
51+
return $this->data;
52+
}
53+
54+
public function isPeriodic()
55+
{
56+
return $this->periodic;
57+
}
58+
59+
public function isActive()
60+
{
61+
return $this->loop->isTimerActive($this);
62+
}
63+
64+
public function cancel()
65+
{
66+
$this->loop->cancelTimer($this);
67+
}
68+
}

Timer/TimerInterface.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace React\EventLoop\Timer;
4+
5+
interface TimerInterface
6+
{
7+
public function getLoop();
8+
public function getInterval();
9+
public function getCallback();
10+
public function setData($data);
11+
public function getData();
12+
public function isPeriodic();
13+
public function isActive();
14+
public function cancel();
15+
}

0 commit comments

Comments
 (0)
0