Commits

Ruslan Osmanov committed 9ea03d7 Merge

Merge branch 'develop'

Comments (0)

Files changed (13)

 	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;
 /* }}} */
 
 /* {{{ php_ev_write_property */
-void php_ev_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
+static void php_ev_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
 {
 	zval                 tmp_member;
 	php_ev_object       *obj;
 /* {{{ php_ev_has_property */
 static int php_ev_has_property(zval *object, zval *member, int has_set_exists, const zend_literal *key TSRMLS_DC)
 {
-	php_ev_object   *obj = (php_ev_object *) zend_objects_get_address(object TSRMLS_CC);
-	int              ret = 0;
+	php_ev_object	*obj = (php_ev_object *) zend_objects_get_address(object TSRMLS_CC);
+	int				 ret = 0;
 	php_ev_prop_handler  p;
 
 	if (zend_hash_find(obj->prop_handler, Z_STRVAL_P(member), Z_STRLEN_P(member) + 1, (void **) &p) == SUCCESS) {
-	    switch (has_set_exists) {
-	        case 2:
-	            ret = 1;
-	            break;
-	        case 1: {
-	                	zval *value = php_ev_read_property(object, member, BP_VAR_IS, key TSRMLS_CC);
-	                	if (value != EG(uninitialized_zval_ptr)) {
-	                	    convert_to_boolean(value);
-	                	    ret = Z_BVAL_P(value)? 1:0;
-	                	    /* refcount is 0 */
-	                	    Z_ADDREF_P(value);
-	                	    zval_ptr_dtor(&value);
-	                	}
-	                	break;
-	                }
-	        case 0:{
-	                   zval *value = php_ev_read_property(object, member, BP_VAR_IS, key TSRMLS_CC);
-	                   if (value != EG(uninitialized_zval_ptr)) {
-	                	   ret = Z_TYPE_P(value) != IS_NULL? 1:0;
-	                	   /* refcount is 0 */
-	                	   Z_ADDREF_P(value);
-	                	   zval_ptr_dtor(&value);
-	                   }
-	                   break;
-	               }
-	        default:
-	               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for has_set_exists");
-	    }
+		switch (has_set_exists) {
+			case 2:
+				ret = 1;
+				break;
+			case 1: {
+						zval *value = php_ev_read_property(object, member, BP_VAR_IS, key TSRMLS_CC);
+						if (value != EG(uninitialized_zval_ptr)) {
+							convert_to_boolean(value);
+							ret = Z_BVAL_P(value)? 1:0;
+							/* refcount is 0 */
+							Z_ADDREF_P(value);
+							zval_ptr_dtor(&value);
+						}
+						break;
+					}
+			case 0:{
+					   zval *value = php_ev_read_property(object, member, BP_VAR_IS, key TSRMLS_CC);
+					   if (value != EG(uninitialized_zval_ptr)) {
+						   ret = Z_TYPE_P(value) != IS_NULL? 1:0;
+						   /* refcount is 0 */
+						   Z_ADDREF_P(value);
+						   zval_ptr_dtor(&value);
+					   }
+					   break;
+				   }
+			default:
+				   php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for has_set_exists");
+		}
 	} else {
-	    zend_object_handlers *std_hnd = zend_get_std_object_handlers();
-	    ret = std_hnd->has_property(object, member, has_set_exists, key TSRMLS_CC);
+		zend_object_handlers *std_hnd = zend_get_std_object_handlers();
+		ret = std_hnd->has_property(object, member, has_set_exists, key TSRMLS_CC);
 	}
 	return ret;
 } /* }}} */
 
 	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) {
  * This is a helper for derived watcher class objects. */
 static void php_ev_watcher_free_storage(ev_watcher *ptr TSRMLS_DC)
 {
-	zval        *data;
+	zval        *data, *self;
 	ev_watcher  *w_next  , *w_prev;
 	php_ev_loop *o_loop;
 
 		o_loop->w = NULL;
 	}
 
-#if 0
-	if (o_loop) {
-		ev_watcher *pw = o_loop->w;
-
-		if (pw == ptr) { /* head of the list */
-			o_loop->w = w_next;
-		} else {
-			while (pw) {
-				if (php_ev_watcher_next(pw) == ptr) {
-					/* pw is the next watcher after ptr */
-					php_ev_watcher_next(pw) = w_next;
-					break;
-				}
-				pw = php_ev_watcher_next(pw);
-			}
-		}
-	}
-#endif
-
 	php_ev_watcher_next(ptr) = php_ev_watcher_prev(ptr) = NULL;
 
 	PHP_EV_FREE_FCALL_INFO(php_ev_watcher_fci(ptr), php_ev_watcher_fcc(ptr));
 		php_ev_watcher_data(ptr) = NULL;
 	}
 
-	zval_ptr_dtor(&php_ev_watcher_self(ptr));
+	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_timer *ptr = (ev_timer *) 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 */
         pfcc_dst = NULL;                                                                        \
     }                                                                                           \
 
-#define PHP_EV_FREE_FCALL_INFO(pfci, pfcc)       \
-    if (pfci && pfcc) {                          \
-        efree(pfcc);                             \
-                                                 \
-        if (ZEND_FCI_INITIALIZED(*pfci)) {       \
-            PHP_EV_FCI_DELREF(pfci);             \
-        }                                        \
-        efree(pfci);                             \
-    }                                            \
+#define PHP_EV_FREE_FCALL_INFO(pfci, pfcc)        \
+    if (pfci && pfcc) {                           \
+        efree(pfcc);                              \
+        pfcc = NULL;                              \
+                                                  \
+        if (ZEND_FCI_INITIALIZED(*pfci)) {        \
+            PHP_EV_FCI_DELREF(pfci);              \
+        }                                         \
+        efree(pfci);                              \
+        pfci = NULL;                              \
+    }
 
 #define PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj) (obj ? (php_ev_loop *) obj->ptr : NULL)
 #define PHP_EV_WATCHER_FETCH_FROM_OBJECT(o)       ((ev_watcher *) o->ptr)
     <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 (UNEXPECTED(!(x))) { \
+			return FAILURE;     \
+		}                       \
+	} while (0);
+
 static inline void php_ev_prop_write_zval(zval **ppz, zval *value)
 {
+#if 0
+	if (!*ppz) {
+		/* if we assign referenced variable, we should separate it */
+		Z_ADDREF_P(value);
+		if (PZVAL_IS_REF(value)) {
+			SEPARATE_ZVAL(&value);
+		}
+		*ppz = value;
+	} else if (PZVAL_IS_REF(*ppz)) {
+		zval garbage = **ppz; /* old value should be destroyed */
+
+		/* To check: can't *ppz be some system variable like error_zval here? */
+		Z_TYPE_PP(ppz) = Z_TYPE_P(value);
+		(*ppz)->value = value->value;
+		if (Z_REFCOUNT_P(value) > 0) {
+			zval_copy_ctor(*ppz);
+		}
+		zval_dtor(&garbage);
+	} else {
+		zval *garbage = *ppz;
+
+		/* if we assign referenced variable, we should separate it */
+		Z_ADDREF_P(value);
+		if (PZVAL_IS_REF(value)) {
+			SEPARATE_ZVAL(&value);
+		}
+		*ppz = value;
+		zval_ptr_dtor(&garbage);
+	}
+
+#else
+
 	if (!*ppz) {
 		MAKE_STD_ZVAL(*ppz);
 	}
 
-    /* Make a copy of the zval, avoid direct binding to the address
-     * of value, since it breaks refcount in php_ev_read_property()
-     * causing further leaks and memory access violations */
+	/* Make a copy of the zval, avoid direct binding to the address
+	 * of value, since it breaks refcount in read_property()
+	 * causing further leaks and memory access violations */
 	REPLACE_ZVAL_VALUE(ppz, value, PZVAL_IS_REF((zval *)value));
+
+#endif
 }
 
 static inline void php_ev_prop_read_zval(zval *pz, zval **retval)
 		return;
 	}
 
-    MAKE_STD_ZVAL(*retval);
-#if 0
-    REPLACE_ZVAL_VALUE(retval, pz, PZVAL_IS_REF((zval *)pz));
-#endif
+	MAKE_STD_ZVAL(*retval);
 	ZVAL_ZVAL(*retval, pz, 1, 0);
 }
 
 /* {{{ ev_loop_prop_data_get_ptr_ptr */
 static zval **ev_loop_prop_data_get_ptr_ptr(php_ev_object *obj TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	if (!obj->ptr) return NULL;
 
 	if (!PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj)->data) {
 		MAKE_STD_ZVAL(PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj)->data);
 /* {{{ ev_loop_prop_data_read  */
 static int ev_loop_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_LOOP_OBJECT_FETCH_FROM_OBJECT(obj)->data;
 
 /* {{{ ev_loop_prop_data_write  */
 static int ev_loop_prop_data_write(php_ev_object *obj, zval *value TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
 	php_ev_prop_write_zval(&(PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(obj))->data, 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_get_ptr_ptr */
 static zval **ev_watcher_prop_data_get_ptr_ptr(php_ev_object *obj TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	if (!obj->ptr) return NULL;
 
 	zval *data = PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj)->data;
 	if (!data) {
 /* {{{ 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_data_write */
 static int ev_watcher_prop_data_write(php_ev_object *obj, zval *value TSRMLS_DC)
 {
-	PHP_EV_ASSERT(obj->ptr);
+	PHP_EV_PROP_REQUIRE(obj->ptr);
 
-	php_ev_prop_write_zval(&(PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj))->data, value);
+	zval **data = &(PHP_EV_WATCHER_FETCH_FROM_OBJECT(obj))->data;
+	php_ev_prop_write_zval(data, value);
 
 	return SUCCESS;
 }
 /* {{{ 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/05_timer.phpt

 Check for EvTimer functionality
 --FILE--
 <?php 
-error_reporting(0);
+//error_reporting(0);
 
 $fudge = 0.02;
 $id = 1;
 $base = Ev::now();
 $prev = Ev::now();
 
+$timer = array();
+$periodic = array();
+
 for ($i = 1; $i <= 5; ++$i) {
 	$t = $i * $i * 1.735435336;
 	$t -= (int) $t;
 
-	$timer = new EvTimer($t, 0, function ($w, $r)
+	$tmp_timer = new EvTimer($t, 0, function ($w, $r)
 		use (&$id, &$prev, $base, $i, $t, $fudge) {
 			$now = Ev::now();
 
 
 			$prev = $now;
 		});
-	$timer->start();
+	$tmp_timer->start();
+	$timer[] = $tmp_timer;
 
 	$t = $i * $i * 1.375475771;
 	$t -= (int) $t;
 
-	$periodic = new EvPeriodic($base + $t, 0, NULL, function ($w, $r)
+	$tmp_periodic = new EvPeriodic($base + $t, 0, NULL, function ($w, $r)
 		use (&$id, &$prev, $base, $i, $t) {
 			$now = Ev::now();
 
 
 			$prev = $now;
 		});
-	$periodic->start();
+	$tmp_periodic->start();
+	$periodic[] = $tmp_periodic;
 }
 
 echo "ok 1\n";
 Ev::run();
 echo "ok 32\n";
+
+$timer = null;
+$periodic = null;
 ?>
 --EXPECTF--
 ok 1

tests/06_keepalive.phpt

 Check for EvWatcher::keepalive() functionality
 --FILE--
 <?php 
-error_reporting(0);
+//error_reporting(0);
 
 $timer = EvTimer::createStopped(1, 0.3, function ($w, $r) {
 	echo "ok 7\n";

tests/09_loop_timer.phpt

 $base = $l->now();
 $prev = $l->now();
 
+$timer = array();
+$periodic = array();
+
 for ($i = 1; $i <= /*125*/25; ++$i) {
 	$t = $i * $i * 1.735435336;
 	$t -= (int) $t;
-	$timer = $l->timer($t, 0, function ($w, $r)
+	$timer[] = $l->timer($t, 0, function ($w, $r)
 		use (&$id, &$prev, $base, $i, $t, $fudge) {
 			$now = $w->getLoop()->now();
 
 
 	$t = $i * $i * 1.375475771;
 	$t -= (int) $t;
-	$periodic = $l->periodic($base + $t, 0, NULL, function ($w, $r)
+	$periodic[] = $l->periodic($base + $t, 0, NULL, function ($w, $r)
 		use (&$id, &$prev, $base, $i, $t, $fudge) {
 			$now = $w->getLoop()->now();
 
 $l->run();
 print "ok 152\n";
 
+$timer = null;
+$periodic = null;
 ?>
 --EXPECTF--
 ok 1

tests/11_watcher_data.phpt

 
 	// store data as ref to object
 	$t1 = $loop->timer(1, 0, function () {});
-	$t1->data = &$i1;
+	$t1->data = $i1;
 
 	// store data as object
 	$t2 = $loop->timer(1, 0, function () {});
-	$t2->data = $i2;
+	$t2->data = &$i2;
 
 	echo "0";
 	$t1->stop();

tests/12_watcher_leak.phpt

+--TEST--
+Check for EvWatcher object destructors(leaks)
+--FILE--
+<?php
+ini_set('memory_limit', '1M');
+
+$limit = 100000;
+
+$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 < $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);
+}
+Ev::run();
+echo $n;
+?>
+--EXPECT--
+cb 1
+100000
+100000
 void php_ev_watcher_callback(EV_P_ ev_watcher *watcher, int revents)
 {
 	zval            **args[2];
-	zval             *key1;
 	zval             *key2;
 	zval             *retval_ptr;
 	zval             *self       = php_ev_watcher_self(watcher);
 	TSRMLS_FETCH_FROM_CTX(php_ev_watcher_thread_ctx(watcher));
 
 	/* libev might have stopped watcher */
-	if (php_ev_watcher_flags(watcher) & PHP_EV_WATCHER_FLAG_UNREFED
-			&& !ev_is_active(watcher)) {
+	if (UNEXPECTED(php_ev_watcher_flags(watcher) & PHP_EV_WATCHER_FLAG_UNREFED
+			&& !ev_is_active(watcher))) {
 		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 */
-		key1 = self;
-		args[0] = &key1;
-		zval_add_ref(&key1);
+		args[0] = &self;
+		Z_ADDREF_P(self);
 
 		MAKE_STD_ZVAL(key2);
 		args[1] = &key2;
 		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,
 		            "An error occurred while invoking the callback");
 		}
 
-		zval_ptr_dtor(&key1);
+		zval_ptr_dtor(&self);
 		zval_ptr_dtor(&key2);
 	}
 }
 
 	ev_init((ev_watcher *) w, (ZEND_FCI_INITIALIZED(*pfci) ? php_ev_watcher_callback : 0));
 
-	Z_ADDREF_P(self);
 	if (data) {
 		Z_ADDREF_P(data);
 	}
 
+#if 0
+	Z_ADDREF_P(self);
+#endif
+
 	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_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_loop_ptr(w)   (php_ev_watcher_loop(w)->loop)
 
-#define PHP_EV_WATCHER_FLAG_KEEP_ALIVE 1
-#define PHP_EV_WATCHER_FLAG_UNREFED    2
-
-#define PHP_EV_WATCHER_UNREF(w)                                                            \
-    if (!(php_ev_watcher_flags(w) &                                                        \
-                (PHP_EV_WATCHER_FLAG_KEEP_ALIVE | PHP_EV_WATCHER_FLAG_UNREFED))            \
-            && ev_is_active(w)) {                                                          \
-        ev_unref(php_ev_watcher_loop(w)->loop);                                            \
-        php_ev_watcher_flags(w) |= PHP_EV_WATCHER_FLAG_UNREFED;                            \
-    }
-
-#define PHP_EV_WATCHER_REF(w)                                                              \
-    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);                                              \
-    }
-
-#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_FLAG_KEEP_ALIVE   (1<<0)
+#define PHP_EV_WATCHER_FLAG_UNREFED      (1<<1)
+#define PHP_EV_WATCHER_FLAG_SELF_UNREFED (1<<2)
+
+#define PHP_EV_WATCHER_SELF_UNREF(w)                                             \
+	do {                                                                         \
+		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)                                        \
+	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 {                                                                            \
+    	if (!(php_ev_watcher_flags(w) &                                             \
+                	(PHP_EV_WATCHER_FLAG_KEEP_ALIVE | PHP_EV_WATCHER_FLAG_UNREFED)) \
+            	&& ev_is_active(w)) {                                               \
+        	ev_unref(php_ev_watcher_loop(w)->loop);                                 \
+        	php_ev_watcher_flags(w) |= PHP_EV_WATCHER_FLAG_UNREFED;                 \
+    	}                                                                           \
+	} 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);                    \
+    	}                                                            \
     } 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_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);                          \
+        }                                                     \
+		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)