Ruslan Osmanov avatar Ruslan Osmanov committed 5b33cf7

Fix: check for attempts for bind a signum to different event loops
Fix: check if signum is positive in EvSignal::__construct

Comments (0)

Files changed (10)

-Handle error when user attempts to attach a signal to different loops
-=====================================================================
-
-There are problems with the following assertion in ev.c:
-
-  assert (("libev: a signal must not be attached to two different loops",
-             !signals [w->signum - 1].loop || signals [w->signum - 1].loop == loop));
-
-Thus, before starting a signal watcher, I have to check it as follows:
-
-	if (signals[(w)->signum - 1].loop
-		&& signals[(w)->signum - 1].loop != my_loop_ptr) {
-		/* error */
-	}
-
-But `signals` declared as static in libev/ev.c:
-
-	static ANSIG signals [EV_NSIG - 1];
-
-So I need my own(MyG()?) array to check this.
-
 Add EvLoop::setInvokePendingCallback(`ev_set_invoke_pending_cb`)?
 =================================================================
 
 static PHP_GINIT_FUNCTION(ev)
 {
 	ev_globals->default_loop = NULL;
+	memset(ev_globals->signal_loops, 0, sizeof(ev_globals->signal_loops));
 }
 /* }}} */
 
 	if (!in_ctor) {
 		/* Factory method mode */
 		if (is_default_loop) {
-
 			if (!*default_loop_ptr_ptr) {
 				loop = ev_default_loop(flags);
 			} else {
 }
 /* }}} */
 
-/* {{{ proto EvLoop EvLoop::defaultLoop([int flags = EVLAG_AUTO[, mixed data = NULL[, double io_interval = 0.[, double timeout_interval = 0.]]]])
+/* {{{ proto EvLoop EvLoop::defaultLoop([int flags = Ev::FLAG_AUTO[, mixed data = NULL[, double io_interval = 0.[, double timeout_interval = 0.]]]])
 */
 PHP_METHOD(EvLoop, defaultLoop)
 {
 }
 /* }}} */
 
-/* {{{ proto EvLoop EvLoop::__construct([int flags = EVLAG_AUTO[[, mixed data = NULL[, double io_interval = 0.[, double timeout_interval = 0.]]]]) */
+/* {{{ proto EvLoop EvLoop::__construct([int flags = Ev::FLAG_AUTO[[, mixed data = NULL[, double io_interval = 0.[, double timeout_interval = 0.]]]]) */
 PHP_METHOD(EvLoop, __construct) 
 {
 	php_ev_loop_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU,
   <notes><![CDATA[
   Fix: get_property_ptr_ptr for zval properties was not supoprted. See
   http://old.nabble.com/Can%27t-get-zval-write-property-handler-working-for-%2B%2B------%2B%2B-and-other-operators-to34843859.html
+  Fix: check for attempts to bind a signal to different event loops
   ]]></notes>
   <!--}}}-->
   <!--{{{ Contents -->
         <file role="src" name="07_stat.phpt"/>
         <file role="src" name="08_priority.phpt"/>
         <file role="src" name="09_loop_timer.phpt"/>
+        <file role="src" name="10_signal.phpt"/>
       </dir>
       <dir name="libev">
         <file role="doc" name="LICENSE"/>
   <notes><![CDATA[
   Fix: get_property_ptr_ptr for zval properties was not supoprted. See
   http://old.nabble.com/Can%27t-get-zval-write-property-handler-working-for-%2B%2B------%2B%2B-and-other-operators-to34843859.html
+  Fix: check for attempts to bind a signal to different event loops
   ]]></notes>
   <!--}}}-->
     <!--{{{ 0.2.0 -->
 PHP_RSHUTDOWN_FUNCTION(ev);
 PHP_MINFO_FUNCTION(ev);
 
+/* Max. signum supported */
+#ifndef EV_NSIG
+# define EV_NSIG 32
+#endif
+
 ZEND_BEGIN_MODULE_GLOBALS(ev)
 	zval *default_loop;
+	/* Helps to prevent binding of different `signum's to a loop */
+	struct ev_loop *signal_loops[EV_NSIG - 1];
 ZEND_END_MODULE_GLOBALS(ev)
 ZEND_EXTERN_MODULE_GLOBALS(ev)
 
 extern zend_module_entry ev_module_entry;
 #define phpext_ev_ptr &ev_module_entry
 
-#define PHP_EV_VERSION "0.2.0"
+#define PHP_EV_VERSION "0.2.1"
 
 #endif /* PHP_EV_H */
 
 		return;
 	}
 
+	PHP_EV_CHECK_SIGNUM(signum);
+
 	if (ctor) {
 		self = getThis();
 	} else {
 	o_self->ptr = (void *) w;
 
 	if (start) {
-		PHP_EV_WATCHER_START(ev_signal, w);
+		PHP_EV_SIGNAL_START(w);
 	}
 }
 /* }}} */

tests/10_signal.phpt

+--TEST--
+Check for EvSignal functionality
+--SKIPIF--
+<?php
+if (!extension_loaded("pcntl")) print "skip pcntl extension is not loaded";
+if (!extension_loaded("posix")) print "skip posix extension is not loaded";
+?>
+--FILE--
+<?php 
+error_reporting(0);
+
+echo "ok 1\n";
+
+$sig1 = new EvSignal(SIGUSR1, function () { echo "ok 8\n"; });
+
+echo "ok 2\n";
+
+$loop = new EvLoop();
+
+echo "ok 3\n";
+
+$usr2_1 = $loop->signal(SIGUSR2, function ($w) { echo "ok 6\n"; $w->stop(); });
+$usr2_2 = $loop->signal(SIGUSR1, function () { echo "not ok 6\n"; });
+
+$sig2 = new EvSignal(SIGUSR2, function () { echo "not ok 8\n"; });
+
+echo "ok 4\n";
+
+posix_kill(posix_getpid(), SIGUSR1);
+posix_kill(posix_getpid(), SIGUSR2);
+
+echo "ok 5\n";
+
+$loop->run();
+
+echo "ok 7\n";
+
+Ev::run(Ev::RUN_ONCE);
+
+echo "ok 9\n";
+
+// Re-bind
+$usr2 = new EVSignal(SIGUSR2, function () { echo "ok 11\n"; });
+$sig1->stop();
+$usr1_2 = $loop->signal(SIGUSR1, function () { echo "ok 13\n"; });
+
+echo "ok 10\n";
+
+posix_kill(posix_getpid(), SIGUSR2);
+
+Ev::run(Ev::RUN_NOWAIT);
+
+echo "ok 12\n";
+
+posix_kill(posix_getpid(), SIGUSR1);
+
+$loop->run(Ev::RUN_NOWAIT);
+
+echo "ok 14\n";
+?>
+--EXPECT--
+ok 1
+ok 2
+ok 3
+ok 4
+ok 5
+ok 6
+ok 7
+ok 8
+ok 9
+ok 10
+ok 11
+ok 12
+ok 13
+ok 14
 	php_ev_get_prop_ptr_ptr_t  get_ptr_ptr_func;
 } php_ev_prop_handler;
 
+
 #endif /* PHP_EV_TYPES_H */
 
 /*
    +----------------------------------------------------------------------+
 */
 
+#if 0
 #include "embed.h"
 #include "priv.h"
 #include "watcher.h"
+#endif
+#include "php_ev.h"
 
 /* Defined in ev.c */
 extern zend_class_entry *ev_loop_class_entry_ptr;
 #endif
 #if EV_SIGNAL_ENABLE
 		case EV_SIGNAL:
-			PHP_EV_WATCHER_START(ev_signal, watcher);
+			PHP_EV_SIGNAL_START((ev_signal *) watcher);
 			break;
 #endif
 #if EV_CHILD_ENABLE
 #endif
 #if EV_SIGNAL_ENABLE
 		case EV_SIGNAL:
-			PHP_EV_WATCHER_STOP(ev_signal, watcher);
+			PHP_EV_SIGNAL_STOP((ev_signal *) watcher);
 			break;
 #endif
 #if EV_CHILD_ENABLE
 
 #define PHP_EV_CHECK_SIGNAL_CAN_START(w)                                                   \
     do {                                                                                   \
-        /* Pulled this part from EV module of Perl */                                      \
-        if (signals [(w)->signum - 1].loop                                                 \
-                && signals [(w)->signum - 1].loop != php_ev_watcher_loop_ptr(w)) {         \
+        struct ev_loop *tmp_loop = MyG(signal_loops[(w)->signum - 1]);                     \
+        if (tmp_loop && tmp_loop != php_ev_watcher_loop_ptr(w)) {                          \
             php_error_docref(NULL TSRMLS_CC, E_WARNING,                                    \
                     "Can't start signal watcher, signal %d already "                       \
-                    "registered in another loop", w->signum);                              \
+                    "registered for another loop", (w)->signum);                           \
+            return;                                                                        \
         }                                                                                  \
     } while (0)
 
 #define PHP_EV_SIGNAL_START(w)                                                             \
     do {                                                                                   \
-        PHP_EV_CHECK_SIGNAL_CAN_START(w);                                                  \
-        PHP_EV_WATCHER_START(ev_signal, w);                                                \
+        PHP_EV_CHECK_SIGNAL_CAN_START(((ev_signal *) (w)));                                \
+        MyG(signal_loops[(w)->signum - 1]) = php_ev_watcher_loop_ptr((w));                 \
+        PHP_EV_WATCHER_START(ev_signal, (w));                                              \
+    } while (0)
+
+#define PHP_EV_SIGNAL_STOP(w)                                                              \
+    do {                                                                                   \
+        MyG(signal_loops[(w)->signum - 1]) = 0;                                            \
+        PHP_EV_WATCHER_STOP(ev_signal, (w));                                               \
     } while (0)
 
 #define PHP_EV_SIGNAL_RESET(w, seta)                                                       \
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.