diff --git a/src/ExtEventLoop.php b/src/ExtEventLoop.php index 48657f96..7160c908 100644 --- a/src/ExtEventLoop.php +++ b/src/ExtEventLoop.php @@ -294,7 +294,7 @@ private function unsubscribeStreamEvent($stream, $flag) */ private function createTimerCallback() { - $this->timerCallback = function ($_, $_, $timer) { + $this->timerCallback = function ($_, $__, $timer) { call_user_func($timer->getCallback(), $timer); if (!$timer->isPeriodic() && $this->isTimerActive($timer)) { diff --git a/tests/AbstractLoopTest.php b/tests/AbstractLoopTest.php index 17163e8b..d8457617 100644 --- a/tests/AbstractLoopTest.php +++ b/tests/AbstractLoopTest.php @@ -2,6 +2,8 @@ namespace React\Tests\EventLoop; +use React\EventLoop\LoopInterface; + abstract class AbstractLoopTest extends TestCase { /** @@ -11,9 +13,18 @@ abstract class AbstractLoopTest extends TestCase public function setUp() { + parent::setUp(); $this->loop = $this->createLoop(); } + protected function tearDown() + { + parent::tearDown(); + if (strncmp($this->getName(false), 'testSignal', 10) === 0 && extension_loaded('pcntl')) { + $this->resetSignalHandlers(); + } + } + abstract public function createLoop(); public function createStream() @@ -495,4 +506,113 @@ private function assertRunFasterThan($maxInterval) $this->assertLessThan($maxInterval, $interval); } + + public function signalProvider() + { + return [ + ['SIGUSR1', SIGUSR1], + ['SIGHUP', SIGHUP], + ['SIGTERM', SIGTERM], + ]; + } + + private $_signalHandled = false; + + /** + * Test signal interrupt when no stream is attached to the loop + * @dataProvider signalProvider + */ + public function testSignalInterruptNoStream($sigName, $signal) + { + if (!extension_loaded('pcntl')) { + $this->markTestSkipped('"pcntl" extension is required to run this test.'); + } + + // dispatch signal handler once before signal is sent and once after + $this->loop->addTimer(0.01, function() { pcntl_signal_dispatch(); }); + $this->loop->addTimer(0.03, function() { pcntl_signal_dispatch(); }); + if (defined('HHVM_VERSION')) { + // hhvm startup is slow so we need to add another handler much later + $this->loop->addTimer(0.5, function() { pcntl_signal_dispatch(); }); + } + + $this->setUpSignalHandler($signal); + + // spawn external process to send signal to current process id + $this->forkSendSignal($signal); + $this->loop->run(); + $this->assertTrue($this->_signalHandled); + } + + /** + * Test signal interrupt when a stream is attached to the loop + * @dataProvider signalProvider + */ + public function testSignalInterruptWithStream($sigName, $signal) + { + if (!extension_loaded('pcntl')) { + $this->markTestSkipped('"pcntl" extension is required to run this test.'); + } + + // dispatch signal handler every 10ms + $this->loop->addPeriodicTimer(0.01, function() { pcntl_signal_dispatch(); }); + + // add stream to the loop + list($writeStream, $readStream) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); + $this->loop->addReadStream($readStream, function($stream, $loop) { + /** @var $loop LoopInterface */ + $read = fgets($stream); + if ($read === "end loop\n") { + $loop->stop(); + } + }); + $this->loop->addTimer(0.05, function() use ($writeStream) { + fwrite($writeStream, "end loop\n"); + }); + + $this->setUpSignalHandler($signal); + + // spawn external process to send signal to current process id + $this->forkSendSignal($signal); + + $this->loop->run(); + + $this->assertTrue($this->_signalHandled); + } + + /** + * add signal handler for signal + */ + protected function setUpSignalHandler($signal) + { + $this->_signalHandled = false; + $this->assertTrue(pcntl_signal($signal, function() { $this->_signalHandled = true; })); + } + + /** + * reset all signal handlers to default + */ + protected function resetSignalHandlers() + { + foreach($this->signalProvider() as $signal) { + pcntl_signal($signal[1], SIG_DFL); + } + } + + /** + * fork child process to send signal to current process id + */ + protected function forkSendSignal($signal) + { + $currentPid = posix_getpid(); + $childPid = pcntl_fork(); + if ($childPid == -1) { + $this->fail("Failed to fork child process!"); + } else if ($childPid === 0) { + // this is executed in the child process + usleep(20000); + posix_kill($currentPid, $signal); + die(); + } + } } diff --git a/tests/StreamSelectLoopTest.php b/tests/StreamSelectLoopTest.php index 61b059c1..55d3d165 100644 --- a/tests/StreamSelectLoopTest.php +++ b/tests/StreamSelectLoopTest.php @@ -2,19 +2,10 @@ namespace React\Tests\EventLoop; -use React\EventLoop\LoopInterface; use React\EventLoop\StreamSelectLoop; class StreamSelectLoopTest extends AbstractLoopTest { - protected function tearDown() - { - parent::tearDown(); - if (strncmp($this->getName(false), 'testSignal', 10) === 0 && extension_loaded('pcntl')) { - $this->resetSignalHandlers(); - } - } - public function createLoop() { return new StreamSelectLoop(); @@ -36,113 +27,4 @@ public function testStreamSelectTimeoutEmulation() $this->assertGreaterThan(0.04, $interval); } - - public function signalProvider() - { - return [ - ['SIGUSR1', SIGUSR1], - ['SIGHUP', SIGHUP], - ['SIGTERM', SIGTERM], - ]; - } - - private $_signalHandled = false; - - /** - * Test signal interrupt when no stream is attached to the loop - * @dataProvider signalProvider - */ - public function testSignalInterruptNoStream($sigName, $signal) - { - if (!extension_loaded('pcntl')) { - $this->markTestSkipped('"pcntl" extension is required to run this test.'); - } - - // dispatch signal handler once before signal is sent and once after - $this->loop->addTimer(0.01, function() { pcntl_signal_dispatch(); }); - $this->loop->addTimer(0.03, function() { pcntl_signal_dispatch(); }); - if (defined('HHVM_VERSION')) { - // hhvm startup is slow so we need to add another handler much later - $this->loop->addTimer(0.5, function() { pcntl_signal_dispatch(); }); - } - - $this->setUpSignalHandler($signal); - - // spawn external process to send signal to current process id - $this->forkSendSignal($signal); - $this->loop->run(); - $this->assertTrue($this->_signalHandled); - } - - /** - * Test signal interrupt when a stream is attached to the loop - * @dataProvider signalProvider - */ - public function testSignalInterruptWithStream($sigName, $signal) - { - if (!extension_loaded('pcntl')) { - $this->markTestSkipped('"pcntl" extension is required to run this test.'); - } - - // dispatch signal handler every 10ms - $this->loop->addPeriodicTimer(0.01, function() { pcntl_signal_dispatch(); }); - - // add stream to the loop - list($writeStream, $readStream) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $this->loop->addReadStream($readStream, function($stream, $loop) { - /** @var $loop LoopInterface */ - $read = fgets($stream); - if ($read === "end loop\n") { - $loop->stop(); - } - }); - $this->loop->addTimer(0.05, function() use ($writeStream) { - fwrite($writeStream, "end loop\n"); - }); - - $this->setUpSignalHandler($signal); - - // spawn external process to send signal to current process id - $this->forkSendSignal($signal); - - $this->loop->run(); - - $this->assertTrue($this->_signalHandled); - } - - /** - * add signal handler for signal - */ - protected function setUpSignalHandler($signal) - { - $this->_signalHandled = false; - $this->assertTrue(pcntl_signal($signal, function() { $this->_signalHandled = true; })); - } - - /** - * reset all signal handlers to default - */ - protected function resetSignalHandlers() - { - foreach($this->signalProvider() as $signal) { - pcntl_signal($signal[1], SIG_DFL); - } - } - - /** - * fork child process to send signal to current process id - */ - protected function forkSendSignal($signal) - { - $currentPid = posix_getpid(); - $childPid = pcntl_fork(); - if ($childPid == -1) { - $this->fail("Failed to fork child process!"); - } else if ($childPid === 0) { - // this is executed in the child process - usleep(20000); - posix_kill($currentPid, $signal); - die(); - } - } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: