Ruslan Osmanov avatar Ruslan Osmanov committed 1579eaa

Fix: memory leaks in objects derived from EvWatcher
Refact: some frequent checks optimized

Comments (0)

Files changed (8)

 	o_loop_other   = (php_ev_object *) zend_object_store_get_object(loop_other TSRMLS_CC);
 	loop_other_ptr = PHP_EV_LOOP_FETCH_FROM_OBJECT(o_loop_other);
 
-	if (!(ev_backend(loop_other_ptr) & ev_embeddable_backends())) {
+	if (UNEXPECTED(!(ev_backend(loop_other_ptr) & ev_embeddable_backends()))) {
         php_error_docref(NULL TSRMLS_CC, E_WARNING,
         		"Passed loop is not embeddable. Check out your backends.");
         return;
 
 	if (obj->ptr) {
 		efree(obj->ptr);
+		obj->ptr = NULL;
 	}
 
 	efree(obj);
 {
 	php_ev_object *obj = (php_ev_object *) obj_;
 
-	PHP_EV_ASSERT(obj->ptr);
+	if (UNEXPECTED(!obj->ptr)) return;
 	php_ev_loop *ptr = (php_ev_loop *) obj->ptr;
 
 	if (ptr->loop) {
 		php_ev_watcher_data(ptr) = NULL;
 	}
 
-	if (php_ev_watcher_self(ptr)) {
-		zval_ptr_dtor(&php_ev_watcher_self(ptr));
-		php_ev_watcher_self(ptr) = NULL;
+	if (php_ev_watcher_self(ptr) && Z_REFCOUNT_P(php_ev_watcher_self(ptr)) > 1) {
+		Z_DELREF_P(php_ev_watcher_self(ptr));
 	}
 }
 /* }}} */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_io *ptr = (ev_io *) obj_ptr->ptr;
 
 	/* Free base class members */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_periodic *ptr              = (ev_periodic *) obj_ptr->ptr;
 	php_ev_periodic *periodic_ptr = (php_ev_periodic *) obj_ptr->ptr;
 
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_signal *ptr = (ev_signal *) obj_ptr->ptr;
 
 	/* Free base class members */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_child *ptr = (ev_child *) obj_ptr->ptr;
 
 	/* Free base class members */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_stat *ptr          = (ev_stat *) obj_ptr->ptr;
 	php_ev_stat *stat_ptr = (php_ev_stat *) obj_ptr->ptr;
 
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_idle *ptr = (ev_idle *) obj_ptr->ptr;
 
 	/* Free base class members */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_check *ptr = (ev_check *) obj_ptr->ptr;
 
 	/* Free base class members */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_prepare *ptr = (ev_prepare *) obj_ptr->ptr;
 
 	/* Free base class members */
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 
 	ev_embed *ptr           = (ev_embed *) obj_ptr->ptr;
 	php_ev_embed *embed_ptr = (php_ev_embed *) obj_ptr->ptr;
 {
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
-	PHP_EV_ASSERT(obj_ptr->ptr);
+	if (UNEXPECTED(!obj_ptr->ptr)) return;
 	ev_fork *ptr = (ev_fork *) obj_ptr->ptr;
 
 	/* Free base class members */
     <email>osmanov@php.net</email>
     <active>yes</active>
   </lead>
-  <date>2013-07-21</date>
+  <date>2013-08-03</date>
   <!--{{{ Current version -->
   <version>
-    <release>0.2.8</release>
+    <release>0.2.9</release>
     <api>0.2.0</api>
   </version>
   <stability>
   </stability>
   <license uri="http://www.php.net/license">PHP</license>
   <notes><![CDATA[
-  Fix: segmentation fault in EvLoop/EvWatcher dtors caused by reference variables stored in 'data' property
-  Fix: EvLoop/EvWatcher object dtors sometimes didn't free the 'data' member until the script shutdown phase
-  Fix: get_properties property handler was not implemented for PHP >= 5.4.0
+  Fix: memory leaks in objects derived from EvWatcher 
+  Refact: some frequent checks optimized
   ]]></notes>
   <!--}}}-->
   <!--{{{ Contents -->
         <file role="src" name="09_loop_timer.phpt"/>
         <file role="src" name="10_signal.phpt"/>
         <file role="src" name="11_watcher_data.phpt"/>
+        <file role="src" name="12_watcher_leak.phpt"/>
         <file role="src" name="bug64788.phpt"/>
       </dir>
       <dir name="libev">
   </extsrcrelease>
   <!--{{{ changelog-->
   <changelog>
+    <!--{{{ 0.2.9 -->
+    <release>
+      <version>
+        <release>0.2.9</release>
+        <api>0.2.0</api>
+      </version>
+      <stability>
+        <release>stable</release>
+        <api>stable</api>
+      </stability>
+      <license uri="http://www.php.net/license">PHP</license>
+      <notes><![CDATA[
+  Fix: memory leaks in objects derived from EvWatcher 
+  Refact: some frequent checks optimized
+  ]]></notes>
+    </release>
+    <!--}}}-->
     <!--{{{ 0.2.8 -->
     <release>
       <version>
 #include "watcher.h"
 #include <fcntl.h>
 
-#define PHP_EV_PROP_REQUIRE(x) \
-	do {                       \
-		if (!(x)) {            \
-			return FAILURE;    \
-		}                      \
+#define PHP_EV_PROP_REQUIRE(x)  \
+	do {                        \
+		if (UNEXPECTED(!(x))) { \
+			return FAILURE;     \
+		}                       \
 	} while (0);
 
 static inline void php_ev_prop_write_zval(zval **ppz, zval *value)
 /* {{{ ev_loop_prop_backend_read */
 static int ev_loop_prop_backend_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_LONG(*retval, ev_backend(PHP_EV_LOOP_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_loop_prop_is_default_loop_read */
 static int ev_loop_prop_is_default_loop_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_BOOL(*retval, ev_is_default_loop(PHP_EV_LOOP_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_loop_prop_iteration_loop_read */
 static int ev_loop_prop_iteration_loop_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_LONG(*retval, ev_iteration(PHP_EV_LOOP_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_loop_prop_pending_loop_read */
 static int ev_loop_prop_pending_loop_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_LONG(*retval, ev_pending_count(PHP_EV_LOOP_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_loop_prop_io_interval_read */
 static int ev_loop_prop_io_interval_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	php_ev_loop *loop_obj = PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj);
 
 {
 	php_ev_loop *loop_obj;
 
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	loop_obj = PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_loop_prop_timeout_interval_read */
 static int ev_loop_prop_timeout_interval_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	php_ev_loop *loop_obj = PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj);
 
 {
 	php_ev_loop *loop_obj;
 
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	loop_obj = PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_loop_prop_depth_read */
 static int ev_loop_prop_depth_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_LONG(*retval, ev_depth(PHP_EV_LOOP_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_watcher_prop_is_active_read */
 static int ev_watcher_prop_is_active_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_BOOL(*retval, ev_is_active(PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_watcher_prop_data_read  */
 static int ev_watcher_prop_data_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	zval *data = PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj)->data;
 
 /* {{{ ev_watcher_prop_is_pending_read */
 static int ev_watcher_prop_is_pending_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_BOOL(*retval, ev_is_pending(PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_watcher_prop_priority_read */
 static int ev_watcher_prop_priority_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 	
 	MAKE_STD_ZVAL(*retval);
 	ZVAL_LONG(*retval, ev_priority(PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj)));
 /* {{{ ev_watcher_prop_priority_write */
 static int ev_watcher_prop_priority_write(php_ev_object *obj, zval *value TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	long priority;
 	ev_watcher *watcher = PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 /* {{{ ev_timer_prop_repeat_read */
 static int ev_timer_prop_repeat_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_timer *timer_watcher = (ev_timer *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 
 	timer_watcher = (ev_timer *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	repeat = Z_DVAL_P(value);
 
 /* {{{ ev_timer_prop_remaining_read */
 static int ev_timer_prop_remaining_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_timer *timer_watcher = (ev_timer *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_periodic_prop_offset_read */
 static int ev_periodic_prop_offset_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_periodic *w = (ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_periodic_prop_offset_write */
 static int ev_periodic_prop_offset_write(php_ev_object *obj, zval *value TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_periodic *w = (ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_periodic_prop_interval_read */
 static int ev_periodic_prop_interval_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_periodic *w = (ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 {
 	double interval;
 
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_periodic *w = (ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 	interval       = (ev_tstamp) Z_DVAL_P(value);
 /* {{{ ev_signal_prop_signum_read */
 static int ev_signal_prop_signum_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_signal *signal_watcher = (ev_signal *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_child_prop_pid_read */
 static int ev_child_prop_pid_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_child *child_watcher = (ev_child *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_child_prop_rpid_read */
 static int ev_child_prop_rpid_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_child *child_watcher = (ev_child *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_child_prop_rstatus_read */
 static int ev_child_prop_rstatus_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_child *child_watcher = (ev_child *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_stat_prop_path_read */
 static int ev_stat_prop_path_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	php_ev_stat *stat_ptr = (php_ev_stat *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_stat_prop_interval_read */
 static int ev_stat_prop_interval_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	ev_stat *stat_watcher = (ev_stat *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 /* {{{ ev_embed_prop_other_read */
 static int ev_embed_prop_other_read(php_ev_object *obj, zval **retval TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	php_ev_embed *embed_ptr = (php_ev_embed *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj);
 
 extern zend_module_entry ev_module_entry;
 #define phpext_ev_ptr &ev_module_entry
 
-#define PHP_EV_VERSION "0.2.8"
+#define PHP_EV_VERSION "0.2.9"
 
 #endif /* PHP_EV_H */
 

tests/12_watcher_leak.phpt

+--TEST--
+Check for EvWatcher object destructors(leaks)
+--FILE--
 <?php
+ini_set('memory_limit', '1M');
 
-error_reporting(E_ALL);
-ini_set('memory_limit', '5M');
+$limit = 100000;
 
-$i = 0;
-$startedAt = microtime(TRUE);
-$callback = function() use (&$i, $startedAt) {
-    if (!(++$i % 2500)) {
-        echo vsprintf("%d\t%d\t%d\t%.4f\t%d\n", $last = [
-            "job" => $i,
-            "mem" => memory_get_usage(),
-            "real" => memory_get_usage(true),
-            "runtime" => $runtime = (microtime(true) - $startedAt),
-            "jps" => ceil($i / $runtime)
-        ]);
-    }
+$callback = function() {
+	static $i = 0;
+	echo "cb ", ++$i, PHP_EOL;
 };
 
+// Create and destroy EvTimer objects in loop.
+// EvTimer's constructor starts the watcher automatically.
+// Thus, eventually only one timer should fire.
 $n = 0;
-while (++$n < 10000) {
-    //$timer = EvTimer::createStopped(-1, 0, $callback);
+while (++$n < $limit) {
     $timer = new EvTimer(-1, 0, $callback);
     //Ev::run(Ev::RUN_NOWAIT | Ev::RUN_ONCE);
+}
+Ev::run();
+echo $n, PHP_EOL;
 
+// Create stopped timers and destroy them in loop.
+// No timer should fire even after Ev::run() call.
+// We're checking whether stopped watchers exhaust the memory.
+$n = 0;
+while (++$n < $limit) {
+    $timer = EvTimer::createStopped(-1, 0, $callback);
+    //Ev::run(Ev::RUN_NOWAIT | Ev::RUN_ONCE);
 }
-// What is the expected behaviour?
-// timers are created and destructed continuosly in the loop.
-// So very few of them will be actually invoked(if any).
-// Isn't it?
 Ev::run();
-echo "n: $n\n";
+echo $n;
+?>
+--EXPECT--
+cb 1
+100000
+100000
 		PHP_EV_WATCHER_REF(watcher);
 	}
 
-	if (revents & EV_ERROR) {
+	if (UNEXPECTED(revents & EV_ERROR)) {
 		int errorno = errno;
 		php_error_docref(NULL TSRMLS_CC, E_WARNING,
 				"Got unspecified libev error in revents, errno = %d, err = %s", errorno, strerror(errorno));
 
 		PHP_EV_EXIT_LOOP(EV_A);
-	} else if (ZEND_FCI_INITIALIZED(*pfci)) {
+	} else if (EXPECTED(ZEND_FCI_INITIALIZED(*pfci))) {
 		/* Setup callback args */
 		args[0] = &self;
 		Z_ADDREF_P(self);
 		pfci->param_count    = 2;
 		pfci->no_separation  = 1;
 
-		if (zend_call_function(pfci, php_ev_watcher_fcc(watcher) TSRMLS_CC) == SUCCESS
-		        && retval_ptr) {
+		if (EXPECTED(zend_call_function(pfci, php_ev_watcher_fcc(watcher) TSRMLS_CC) == SUCCESS
+		        && retval_ptr)) {
 		    zval_ptr_dtor(&retval_ptr);
 		} else {
 		    php_error_docref(NULL TSRMLS_CC, E_WARNING,
 	php_ev_watcher_self(w)  = self;
 	php_ev_watcher_data(w)  = data;
 	php_ev_watcher_loop(w)  = o_loop;
-	php_ev_watcher_flags(w) = PHP_EV_WATCHER_FLAG_KEEP_ALIVE | PHP_EV_WATCHER_FLAG_SELF_UNREFED;
+	php_ev_watcher_flags(w) = PHP_EV_WATCHER_FLAG_KEEP_ALIVE /*| PHP_EV_WATCHER_FLAG_SELF_UNREFED*/;
 
 	PHP_EV_COPY_FCALL_INFO(php_ev_watcher_fci(w), php_ev_watcher_fcc(w), pfci, pfcc);
 
  */
 void *php_ev_new_watcher(size_t size, zval *self, php_ev_loop *o_loop, const zend_fcall_info *pfci, const zend_fcall_info_cache *pfcc, zval *data, int priority TSRMLS_DC)
 {
-	void *w = emalloc(size);
-	memset(w, 0, size);
+	void *w = ecalloc(1, size);
 
 	php_ev_set_watcher((ev_watcher *) w, size, self, o_loop, pfci, pfcc, data, priority TSRMLS_CC);
 
 
 #define PHP_EV_WATCHER_SELF_UNREF(w)                                             \
 	do {                                                                         \
-		if (php_ev_watcher_self(w)   \
-				&& !(php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_SELF_UNREFED)) { \
-			zval_ptr_dtor(&php_ev_watcher_self(w));                              \
+		if (EXPECTED(php_ev_watcher_self(w) != NULL)                             \
+				&& !(php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_SELF_UNREFED) \
+				&&  Z_REFCOUNT_P(php_ev_watcher_self(w)) > 2) {                  \
+			Z_DELREF_P(php_ev_watcher_self(w));                                  \
         	php_ev_watcher_flags(w) |= PHP_EV_WATCHER_FLAG_SELF_UNREFED;         \
 		}                                                                        \
 	} while (0)
 
 #define PHP_EV_WATCHER_SELF_REF(w)                                        \
-    	if (php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_SELF_UNREFED) { \
-        	Z_ADDREF_P(php_ev_watcher_self(w));                           \
+	do {                                                                  \
+		if ((php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_SELF_UNREFED)  \
+				&& Z_REFCOUNT_P(php_ev_watcher_self(w)) < 2) {            \
+    		Z_ADDREF_P(php_ev_watcher_self(w));                           \
         	php_ev_watcher_flags(w) &= ~PHP_EV_WATCHER_FLAG_SELF_UNREFED; \
-        }                                                                 \
+    	}                                                                 \
+	} while (0)
 
 #define PHP_EV_WATCHER_UNREF(w)                                                     \
 	do {                                                                            \
         	ev_unref(php_ev_watcher_loop(w)->loop);                                 \
         	php_ev_watcher_flags(w) |= PHP_EV_WATCHER_FLAG_UNREFED;                 \
     	}                                                                           \
-		PHP_EV_WATCHER_SELF_REF(w);                                               \
 	} while (0)
 
-#define PHP_EV_WATCHER_REF(w)                                             \
-	do {                                                                  \
-    	if (php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_UNREFED) {      \
-        	php_ev_watcher_flags(w) &= ~PHP_EV_WATCHER_FLAG_UNREFED;      \
-        	ev_ref(php_ev_watcher_loop(w)->loop);                         \
-    	}                                                                 \
-		PHP_EV_WATCHER_SELF_UNREF(w);                                       \
+#define PHP_EV_WATCHER_REF(w)                                        \
+	do {                                                             \
+    	if (php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_UNREFED) { \
+        	php_ev_watcher_flags(w) &= ~PHP_EV_WATCHER_FLAG_UNREFED; \
+        	ev_ref(php_ev_watcher_loop(w)->loop);                    \
+    	}                                                            \
     } while (0)
 
 
-#define PHP_EV_WATCHER_STOP(t, w)                                                          \
-    do {                                                                                   \
-        if (php_ev_watcher_loop(w)) {                                                      \
-            PHP_EV_WATCHER_REF(w);                                                         \
-            t ## _stop(php_ev_watcher_loop_ptr(w), (t *) w);                               \
-        }                                                                                  \
+#define PHP_EV_WATCHER_STOP(t, w)                            \
+    do {                                                     \
+        if (EXPECTED(php_ev_watcher_loop(w) != NULL)) {      \
+            PHP_EV_WATCHER_REF(w);                           \
+            t ## _stop(php_ev_watcher_loop_ptr(w), (t *) w); \
+        }                                                    \
+		PHP_EV_WATCHER_SELF_UNREF(w);                        \
     } while (0)
 
-#define PHP_EV_WATCHER_START(t, w)                                                         \
-    do {                                                                                   \
-        if (php_ev_watcher_loop(w)) {                                                      \
-            t ## _start(php_ev_watcher_loop_ptr(w), (t *) w);                              \
-            PHP_EV_WATCHER_UNREF(w);                                                       \
-        }                                                                                  \
+#define PHP_EV_WATCHER_START(t, w)                            \
+    do {                                                      \
+        if (php_ev_watcher_loop(w)) {                         \
+            t ## _start(php_ev_watcher_loop_ptr(w), (t *) w); \
+            PHP_EV_WATCHER_UNREF(w);                          \
+        }                                                     \
+		PHP_EV_WATCHER_SELF_REF(w);                       \
     } while (0)
 
 /* Stop, ev_*_set() and start a watcher. Call it when need
  * to modify probably active watcher.
  * args - list of args for ev_*_set() with brackets */
-#define PHP_EV_WATCHER_RESET(t, w, args)                                                   \
-    do {                                                                                   \
-        int is_active = ev_is_active(w);                                                   \
-                                                                                           \
-        if (is_active) {                                                                   \
-            PHP_EV_WATCHER_STOP(t, w);                                                     \
-        }                                                                                  \
-                                                                                           \
-        t ## _set args;                                                                    \
-                                                                                           \
-        if (is_active) {                                                                   \
-            PHP_EV_WATCHER_START(t, w);                                                    \
-        }                                                                                  \
+#define PHP_EV_WATCHER_RESET(t, w, args) \
+    do {                                 \
+        int is_active = ev_is_active(w); \
+                                         \
+        if (is_active) {                 \
+            PHP_EV_WATCHER_STOP(t, w);   \
+        }                                \
+                                         \
+        t ## _set args;                  \
+                                         \
+        if (is_active) {                 \
+            PHP_EV_WATCHER_START(t, w);  \
+        }                                \
     } while (0)
 
 
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.