Commits

Ruslan Osmanov  committed 0f86ed9

Many memory usage fixes and enhancements
Add: EventBufferEvent ref and free methods
examples/listener.php: converted to oop api

  • Participants
  • Parent commits 641dbb3

Comments (0)

Files changed (8)

File classes/buffer_event.c

 static zend_always_inline void bevent_rw_cb(struct bufferevent *bevent, php_event_bevent_t *bev, zend_fcall_info *pfci, zend_fcall_info_cache *pfcc)
 {
 	PHP_EVENT_ASSERT(bev);
+	PHP_EVENT_ASSERT(bevent == bev->bevent);
 	PHP_EVENT_ASSERT(pfci && pfcc);
+	PHP_EVENT_ASSERT(bev->self);
 
-	zval  *arg_data   = bev->data;
-	zval  *arg_bevent;
+	zval  *arg_data = bev->data;
 	zval **args[2];
 	zval  *retval_ptr;
 
 
 	if (ZEND_FCI_INITIALIZED(*pfci)) {
 		/* Setup callback args */
-		MAKE_STD_ZVAL(arg_bevent);
-
-		PHP_EVENT_ASSERT(bev->self);
 
 		if (bev->self) {
-			ZVAL_ZVAL(arg_bevent, bev->self, 1, 0);
-		} else {
-			ZVAL_NULL(arg_bevent);
+			args[0] = &bev->self;
+			Z_ADDREF_P(bev->self);
 		}
-		args[0] = &arg_bevent;
-
 		if (arg_data) {
 			Z_ADDREF_P(arg_data);
 		} else {
                     "An error occurred while invoking the callback");
         }
 
-        zval_ptr_dtor(&arg_bevent);
         zval_ptr_dtor(&arg_data);
 	}
 }
 	zend_fcall_info_cache *pfcc = bev->fcc_event;
 
 	PHP_EVENT_ASSERT(pfci && pfcc);
+	PHP_EVENT_ASSERT(bev->bevent == bevent);
+	PHP_EVENT_ASSERT(bev->self);
 
 	zval  *arg_data   = bev->data;
-	zval  *arg_bevent;
 	zval  *arg_events;
 	zval **args[3];
 	zval  *retval_ptr;
 
 	if (ZEND_FCI_INITIALIZED(*pfci)) {
 		/* Setup callback args */
-		MAKE_STD_ZVAL(arg_bevent);
-
-		PHP_EVENT_ASSERT(bev->self);
-
-		if (bev->self) {
-			ZVAL_ZVAL(arg_bevent, bev->self, 1, 0);
-		} else {
-			ZVAL_NULL(arg_bevent);
-		}
-		args[0] = &arg_bevent;
+		args[0] = &bev->self;
+		Z_ADDREF_P(bev->self);
 
 		MAKE_STD_ZVAL(arg_events);
 		ZVAL_LONG(arg_events, events);
                     "An error occurred while invoking the callback");
         }
 
-        zval_ptr_dtor(&arg_bevent);
         zval_ptr_dtor(&arg_events);
         zval_ptr_dtor(&arg_data);
 	}
 		fd = php_event_zval_to_fd(ppzfd TSRMLS_CC);
 
 		if (fd < 0) {
-			RETURN_FALSE;
+			return;
 		}
 		/* Make sure that the socket is in non-blocking mode(libevent's tip) */
 		evutil_make_socket_nonblocking(fd);
 	}
 
 	bev->self = zself;
-#if 0
-	Z_ADDREF_P(zself);
-#endif
+}
+/* }}} */
 
-#if 0
-	/* Make sure base destroyed after the bufferevent
-	 * XXX Really need this? */
-	Z_ADDREF_P(zbase);
-#endif
+/* {{{ proto void EventBufferEvent::ref(void); */
+PHP_METHOD(EventBufferEvent, ref)
+{
+	Z_ADDREF_P(getThis());
+}
+/* }}} */
+
+/* {{{ proto void EventBufferEvent::free(void); */
+PHP_METHOD(EventBufferEvent, free)
+{
+	zval               *zbevent = getThis();
+	php_event_bevent_t *bev;
+
+	PHP_EVENT_FETCH_BEVENT(bev, zbevent);
+
+	bufferevent_free(bev->bevent);
+	bev->bevent = 0;
+
+	zval_ptr_dtor(&zbevent);
 }
 /* }}} */
 

File classes/listener.c

 
 /* {{{ _php_event_listener_cb */
 static void _php_event_listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *ctx) {
-
-	php_event_listener_t  *l = (php_event_listener_t *) ctx;
+	php_event_listener_t *l = (php_event_listener_t *) ctx;
 
 	PHP_EVENT_ASSERT(l);
 
 	PHP_EVENT_ASSERT(pfci && pfcc);
 
 	zval  **args[4];
-	zval   *arg_listener;
 	zval   *arg_fd;
 	zval   *arg_address;
 	zval   *arg_data     = l->data;
 	 */
 
 	if (ZEND_FCI_INITIALIZED(*pfci)) {
-		MAKE_STD_ZVAL(arg_listener);
-		ZVAL_ZVAL(arg_listener, l->self, 0, 0);
-		Z_ADDREF_P(l->self);
-		args[0] = &arg_listener;
+		args[0] = &l->self;
+		/*Z_ADDREF_P(l->self);*/
 
 		/* Convert the socket created by libevent to PHP stream
 	 	 * and save it's resource ID in l->stream_id */
 				l->stream_id = Z_LVAL_P(arg_fd);
 				zend_list_addref(l->stream_id);
 			} else {
-				l->stream_id = -1;
 				ALLOC_INIT_ZVAL(arg_fd);
+				l->stream_id = -1;
 			}
 		}
 		args[1] = &arg_fd;
                     "An error occurred while invoking the callback");
         }
 
-        zval_ptr_dtor(&arg_listener);
         zval_ptr_dtor(&arg_fd);
         zval_ptr_dtor(&arg_address);
         zval_ptr_dtor(&arg_data);
 	PHP_EVENT_ASSERT(pfci && pfcc);
 
 	zval  **args[2];
-	zval   *arg_listener;
 	zval   *arg_data     = l->data;
 	zval   *retval_ptr;
 
 	 * void cb (resource $listener, mixed $data); */
 
 	if (ZEND_FCI_INITIALIZED(*pfci)) {
-		MAKE_STD_ZVAL(arg_listener);
-		ZVAL_ZVAL(arg_listener, l->self, 0, 0);
-		Z_ADDREF_P(l->self);
-		args[0] = &arg_listener;
+		args[0] = &l->self;
 
 		if (arg_data) {
 			Z_ADDREF_P(arg_data);
                     "An error occurred while invoking the callback");
         }
 
-        zval_ptr_dtor(&arg_listener);
         zval_ptr_dtor(&arg_data);
 	}
 }
 		/* Make sure that the socket is in non-blocking mode(libevent's tip) */
 		evutil_make_socket_nonblocking(fd);
 
-		PHP_EVENT_FETCH_LISTENER(l, getThis());
+		PHP_EVENT_FETCH_LISTENER(l, zself);
 
 		listener = evconnlistener_new(base->base, _php_event_listener_cb,
 				(void *) l, flags, backlog, fd);
 
 	l->stream_id = -1;
 
-	l->base = zbase;
-	Z_ADDREF_P(zbase);
-
 	l->self = zself;
-	Z_ADDREF_P(zself);
 
 	TSRMLS_SET_CTX(l->thread_ctx);
 }
 			zval_ptr_dtor(&l->data);
 		}
 
-		Z_ADDREF_P(zarg);
 		l->data = zarg;
+		Z_ADDREF_P(zarg);
 	}
 
 	/*
 {
 	php_event_listener_t *l;
 	zval                 *zlistener = getThis();
+	php_event_base_t     *b;
 
 	if (zend_parse_parameters_none() == FAILURE) {
 		return;
 
 	/* base = evconnlistener_get_base(l->listener); */
 
-	if (l->base) {
-		RETURN_ZVAL(l->base, 1, 0);
-	}
+	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_base_ce);
+	PHP_EVENT_FETCH_BASE(b, return_value);
+	/* Don't do this. It's normal to have refcount = 1 here.
+	 * If we got bugs, we most likely free'd an internal buffer somewhere
+	 * Z_ADDREF_P(return_value);*/
 
-	RETVAL_FALSE;
+	b->base = evconnlistener_get_base(l->listener);
+	b->internal = 1;
 }
 /* }}} */
 #endif

File examples/listener.php

  * Usage:
  * 1) In one terminal window run:
  *
- * $ php listener.php
+ * $ php listener.php 9881
  *
  * 2) In another terminal window open up connection, e.g.:
  *
- * $ nc 127.0.0.1 9876
+ * $ nc 127.0.0.1 9881
  *
  * 3) start typing. The server should repeat the input.
  */
 
 function echo_read_cb($bev, $ctx) {
     /* This callback is invoked when there is data to read on $bev. */
-    $input  = bufferevent_get_input($bev);
-    $output = bufferevent_get_output($bev);
+    $input  = $bev->getInput();
+    $output = $bev->getOutput();
 
     /* Copy all the data from the input buffer to the output buffer. */
-    evbuffer_add_buffer($output, $input);
+    EventBuffer::addBuffer($output, $input);
 }
 
 function echo_event_cb($bev, $events, $ctx) {
-    if ($events & EVENT_BEV_EVENT_ERROR)
-        echo "Error from bufferevent";
-    if ($events & (EVENT_BEV_EVENT_EOF | EVENT_BEV_EVENT_ERROR)) {
-        bufferevent_free($bev);
+    if ($events & EventBufferEvent::ERROR)
+        echo "Error from bufferevent\n";
+
+    if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
+        $bev->free();
     }
 }
 
 function accept_conn_cb($listener, $fd, $address, $ctx) {
     /* We got a new connection! Set up a bufferevent for it. */
+    $base = $ctx;
+    //$base = $listener->getBase();
 
-    //$base = $ctx;
-    $base = evconnlistener_get_base($listener);
+    $bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);
 
-    $bev = bufferevent_socket_new($base, $fd, EVENT_BEV_OPT_CLOSE_ON_FREE);
+    $bev->setCallbacks("echo_read_cb", NULL, "echo_event_cb", NULL);
 
-    bufferevent_setcb($bev, "echo_read_cb", NULL, "echo_event_cb", NULL);
+    $bev->enable(Event::READ | Event::WRITE);
 
-    bufferevent_enable($bev, EVENT_READ | EVENT_WRITE);
+	$bev->ref();
 }
 
 function accept_error_cb($listener, $ctx) {
-    $base = evconnlistener_get_base($listener);
+    $base = $listener->getBase();
 
     fprintf(STDERR, "Got an error %d (%s) on the listener. "
         ."Shutting down.\n",
-		event_socket_get_last_errno(),
-		event_socket_get_last_error());
+		EventUtil::getLastSocketErrno(),
+		EventUtil::getLastSocketError());
 
-    event_base_loopexit($base, NULL);
+    $base->exit(NULL);
 }
 
-
-$port = 9876;
+$port = 9808;
 
 if ($argc > 1) {
     $port = (int) $argv[1];
     return 1;
 }
 
-$base = event_base_new();
+$base = new EventBase();
 if (!$base) {
     echo "Couldn't open event base";
 	exit(1);
 	echo "Unable to bind socket\n";
 	exit(1);
 }
-$listener = evconnlistener_new($base, "accept_conn_cb", $base,
-    EVENT_LEV_OPT_CLOSE_ON_FREE | EVENT_LEV_OPT_REUSEABLE, -1, $socket);
+$listener = new EventListener($base, "accept_conn_cb", $base,
+    EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1, $socket);
 
 /* Variant #2 */
 /*
-$listener = evconnlistener_new_bind($base, "accept_conn_cb", $base,
-    EVENT_LEV_OPT_CLOSE_ON_FREE | EVENT_LEV_OPT_REUSEABLE, -1, "0.0.0.0:$port");
+$listener = new EventListener($base, "accept_conn_cb", $base,
+	EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1, "0.0.0.0:$port");
  */
 
 if (!$listener) {
     echo "Couldn't create listener";
 	exit(1);
 }
-evconnlistener_set_error_cb($listener, "accept_error_cb");
+$listener->setErrorCallback("accept_error_cb");
 
-event_base_dispatch($base);
+$base->dispatch();
 
 /* {{{ Private functions */
 
+/* {{{ event_bevent_object_dtor
+ * Required to cleanup bufferevent properly */
+static void event_bevent_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_bevent_t *b = (php_event_bevent_t *) ptr;
+
+	PHP_EVENT_ASSERT(b);
+
+	if (b->bevent) {
+		bufferevent_free(b->bevent);
+		b->bevent = NULL;
+	}
+
+	PHP_EVENT_FREE_FCALL_INFO(b->fci_read,  b->fcc_read);
+	PHP_EVENT_FREE_FCALL_INFO(b->fci_write, b->fcc_write);
+	PHP_EVENT_FREE_FCALL_INFO(b->fci_event, b->fcc_event);
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_object_dtor
+ * Required to cleanup event properly */
+static void event_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_t *e = (php_event_t *) ptr;
+
+	if (e && e->event) {
+		event_del(e->event);
+		event_free(e->event);
+		e->event = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_base_object_dtor
+ * Required to cleanup event base properly */
+static void event_base_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_base_t *b = (php_event_base_t *) ptr;
+
+	if (!b->internal && b->base) {
+		/* TODO: what if events bound to the event_base are not destroyed? */
+		event_base_free(b->base);
+		b->base = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_config_object_dtor
+ * Required to cleanup event config properly */
+static void event_config_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_config_t *cfg = (php_event_config_t *) ptr;
+
+	if (cfg && cfg->ptr) {
+		event_config_free(cfg->ptr);
+		cfg->ptr = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_buffer_object_dtor
+ * Required to cleanup buffer properly */
+static void event_buffer_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_buffer_t *b = (php_event_buffer_t *) ptr;
+
+	/* If we got the buffer in, say, a read callback the buffer
+	 * is destroyed when the callback is done as any normal variable.
+	 * Zend MM calls destructor which eventually calls this function.
+	 * We'll definitely crash, if we call evbuffer_free() on an internal
+	 * bufferevent buffer. */
+
+	if (!b->internal && b->buf) {
+		evbuffer_free(b->buf);
+		b->buf = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_dns_base_object_dtor
+ * Required to cleanup buffer properly */
+static void event_dns_base_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_dns_base_t *dnsb = (php_event_dns_base_t *) ptr;
+
+	if (dnsb && dnsb->dns_base) {
+		/* Setting fail_requests to 1 makes all in-flight requests get
+	 	 * their callbacks invoked with a canceled error code before it
+	 	 * frees the base*/
+		evdns_base_free(dnsb->dns_base, 1);
+		dnsb->dns_base = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_listener_object_dtor
+ * Required to cleanup buffer properly */
+static void event_listener_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_listener_t *l = (php_event_listener_t *) ptr;
+
+	if (l && l->listener) {
+		evconnlistener_free(l->listener);
+		l->listener = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_http_conn_object_dtor
+ * Required to cleanup http conn obj properly */
+static void event_http_conn_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_http_conn_t *evcon = (php_event_http_conn_t *) ptr;
+
+	if (evcon && evcon->conn) {
+		evhttp_connection_free(evcon->conn);
+		evcon->conn = NULL;
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_http_object_dtor
+ * Required to cleanup http obj properly */
+static void event_http_object_dtor(void *ptr, zend_object_handle handle TSRMLS_DC)
+{
+	php_event_http_t *http = (php_event_http_t *) ptr;
+
+	if (http && http->ptr) {
+		evhttp_free(http->ptr);
+	}
+
+	zend_objects_destroy_object(ptr, handle TSRMLS_CC);
+}
+/* }}} */
+
+
 /* {{{ event_generic_object_free_storage */
 static zend_always_inline void event_generic_object_free_storage(void *ptr TSRMLS_DC)
 {
 {
 	php_event_t *e = (php_event_t *) ptr;
 
-	PHP_EVENT_ASSERT(e && e->event);
+	PHP_EVENT_ASSERT(e);
 
 	if (e->data) {
 		zval_ptr_dtor(&e->data);
-#if 0
-		Z_DELREF_P(e->data);
-#endif
 	}
 
 	PHP_EVENT_FREE_FCALL_INFO(e->fci, e->fcc);
 		zend_list_delete(e->stream_id);
 	}
 
-	event_del(e->event);
-	event_free(e->event);
+	if (e->event) {
+		event_del(e->event);
+		event_free(e->event);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 {
 	php_event_base_t *b = (php_event_base_t *) ptr;
 
-	PHP_EVENT_ASSERT(b && b->base);
+	PHP_EVENT_ASSERT(b);
 
-	/* TODO: what if events bound to the event_base are not destroyed? */
-	event_base_free(b->base);
+	if (!b->internal && b->base) {
+		/* TODO: what if events bound to the event_base are not destroyed? */
+		event_base_free(b->base);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 {
 	php_event_config_t *cfg = (php_event_config_t *) ptr;
 
-	PHP_EVENT_ASSERT(cfg && cfg->ptr);
+	PHP_EVENT_ASSERT(cfg);
 
-	event_config_free(cfg->ptr);
+	if (cfg->ptr) {
+		event_config_free(cfg->ptr);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 {
 	php_event_bevent_t *b = (php_event_bevent_t *) ptr;
 
-	PHP_EVENT_ASSERT(b && b->bevent);
-
-	if (b->data) {
-		zval_ptr_dtor(&b->data);
-#if 0
-		Z_DELREF_P(b->data);
-#endif
-	}
+	if (b) {
+		if (b->data) {
+			zval_ptr_dtor(&b->data);
+		}
 
-	PHP_EVENT_FREE_FCALL_INFO(b->fci_read,  b->fcc_read);
-	PHP_EVENT_FREE_FCALL_INFO(b->fci_write, b->fcc_write);
-	PHP_EVENT_FREE_FCALL_INFO(b->fci_event, b->fcc_event);
+		PHP_EVENT_FREE_FCALL_INFO(b->fci_read,  b->fcc_read);
+		PHP_EVENT_FREE_FCALL_INFO(b->fci_write, b->fcc_write);
+		PHP_EVENT_FREE_FCALL_INFO(b->fci_event, b->fcc_event);
 
-	if (b->stream_id >= 0) { /* stdin fd == 0 */
-		zend_list_delete(b->stream_id);
-	}
+		if (b->stream_id >= 0) { /* stdin fd == 0 */
+			zend_list_delete(b->stream_id);
+		}
 
-	if (b->self) {
-#if 0
-		Z_DELREF_P(b->self);
-#endif
-		zval_ptr_dtor(&b->data);
-	}
+		if (b->self) {
+			zval_ptr_dtor(&b->self);
+		}
 
-	bufferevent_free(b->bevent);
+		if (b->bevent) {
+			bufferevent_free(b->bevent);
+		}
 
-	event_generic_object_free_storage(ptr TSRMLS_CC);
+		event_generic_object_free_storage(ptr TSRMLS_CC);
+	}
 }
 /* }}} */
 
 {
 	php_event_buffer_t *b = (php_event_buffer_t *) ptr;
 
-	PHP_EVENT_ASSERT(b && b->buf);
+	PHP_EVENT_ASSERT(b);
 
 	/* If we got the buffer in, say, a read callback the buffer
 	 * is destroyed when the callback is done as any normal variable.
 	 * We'll definitely crash, if we call evbuffer_free() on an internal
 	 * bufferevent buffer. */
 
-	if (!b->internal) {
+	if (!b->internal && b->buf) {
 		evbuffer_free(b->buf);
-
-		event_generic_object_free_storage(ptr TSRMLS_CC);
 	}
+
+	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_dns_base_t *dnsb = (php_event_dns_base_t *) ptr;
 
-	PHP_EVENT_ASSERT(dnsb && dnsb->dns_base);
+	PHP_EVENT_ASSERT(dnsb);
 
-	/* Setting fail_requests to 1 makes all in-flight requests get
-	 * their callbacks invoked with a canceled error code before it
-	 * frees the base*/
-	evdns_base_free(dnsb->dns_base, 1);
+	if (dnsb->dns_base) {
+		/* Setting fail_requests to 1 makes all in-flight requests get
+	 	 * their callbacks invoked with a canceled error code before it
+	 	 * frees the base*/
+		evdns_base_free(dnsb->dns_base, 1);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 {
 	php_event_listener_t *l = (php_event_listener_t *) ptr;
 
-	PHP_EVENT_ASSERT(l && l->listener);
+	PHP_EVENT_ASSERT(l);
+
 	if (l->stream_id >= 0) {
 		zend_list_delete(l->stream_id);
 	}
 
-	if (l->base) {
-#if 0
-		Z_DELREF_P(l->base);
-#endif
-		zval_ptr_dtor(&l->base);
-	}
-
 	if (l->data) {
-#if 0
-		Z_DELREF_P(l->data);
-#endif
 		zval_ptr_dtor(&l->data);
 	}
 
 	if (l->self) {
-#if 0
-		Z_DELREF_P(l->self);
-#endif
 		zval_ptr_dtor(&l->self);
 	}
 
 	PHP_EVENT_FREE_FCALL_INFO(l->fci, l->fcc);
 	PHP_EVENT_FREE_FCALL_INFO(l->fci_err, l->fcc_err);
 
-	evconnlistener_free(l->listener);
+	if (l->listener) {
+		evconnlistener_free(l->listener);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 {
 	php_event_http_conn_t *evcon = (php_event_http_conn_t *) ptr;
 
-	PHP_EVENT_ASSERT(evcon && evcon->conn);
+	PHP_EVENT_ASSERT(evcon);
 
 	if (evcon->base) {
-#if 0
-		Z_DELREF_P(evcon->base);
-#endif
 		zval_ptr_dtor(&evcon->base);
 	}
 
 	if (evcon->dns_base) {
-#if 0
-		Z_DELREF_P(evcon->dns_base);
-#endif
 		zval_ptr_dtor(&evcon->dns_base);
 	}
 
-	evhttp_connection_free(evcon->conn);
+	if (evcon->conn) {
+		evhttp_connection_free(evcon->conn);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 {
 	php_event_http_t *http = (php_event_http_t *) ptr;
 
-	PHP_EVENT_ASSERT(http && http->ptr);
+	PHP_EVENT_ASSERT(http);
 
 	if (http->base) {
-		Z_DELREF_P(http->base);
+		zval_ptr_dtor(&http->base);
 	}
 
-	evhttp_free(http->ptr);
+	if (http->ptr) {
+		evhttp_free(http->ptr);
+	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 
 
 /* {{{ register_object */
-static zend_always_inline zend_object_value register_object(zend_class_entry *ce, void *obj, zend_objects_free_object_storage_t func_free_storage TSRMLS_DC)
+static zend_always_inline zend_object_value register_object(zend_class_entry *ce, void *obj, zend_objects_store_dtor_t func_dtor, zend_objects_free_object_storage_t func_free_storage TSRMLS_DC)
 {
 	zend_object_value retval;
 
-	retval.handle = zend_objects_store_put(obj,
-			(zend_objects_store_dtor_t) zend_objects_destroy_object,
-			func_free_storage, NULL TSRMLS_CC);
+	retval.handle   = zend_objects_store_put(obj, func_dtor, func_free_storage, NULL TSRMLS_CC);
 	retval.handlers = &object_handlers;
 
 	return retval;
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_object_dtor,
+			event_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_base_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_base_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_base_object_dtor,
+			event_base_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_config_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_config_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_config_object_dtor,
+			event_config_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_bevent_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_bevent_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_bevent_object_dtor,
+			event_bevent_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_buffer_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_buffer_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_buffer_object_dtor,
+			event_buffer_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_abstract_object_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_generic_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, (zend_objects_store_dtor_t) zend_objects_destroy_object,
+			event_generic_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_dns_base_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_dns_base_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_dns_base_object_dtor,
+			event_dns_base_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_listener_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_listener_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_listener_object_dtor,
+			event_listener_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_http_conn_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_http_conn_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_http_conn_object_dtor,
+			event_http_conn_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 {
 	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_http_t) TSRMLS_CC);
 
-	return register_object(ce, (void *) obj, event_http_object_free_storage TSRMLS_CC);
+	return register_object(ce, (void *) obj, event_http_object_dtor,
+			event_http_object_free_storage TSRMLS_CC);
 }
 /* }}} */
 
 # endif
 #endif
 
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_util_ce, LIBEVENT_VERSION_NUMBER, LIBEVENT_VERSION_NUMBER);
+
 	/* Handle libevent's error logging more gracefully than it's default
 	 * logging to stderr, or calling abort()/exit() */
 	event_set_fatal_callback(fatal_error_cb);
 #define PHP_EVENT_FREE_FCALL_INFO(pfci, pfcc)                                                    \
     if (pfci && pfcc) {                                                                          \
         efree(pfcc);                                                                             \
+        pfcc = NULL;                                                                             \
                                                                                                  \
         if (ZEND_FCI_INITIALIZED(*pfci)) {                                                       \
             PHP_EVENT_FCI_DELREF(pfci);                                                          \
         }                                                                                        \
         efree(pfci);                                                                             \
+        pfci = NULL;                                                                             \
     }                                                                                            \
 
 #define PHP_EVENT_LIBEVENT_VERSION_REQUIRED(func, v)                                           \
 /* }}} */
 
 const zend_function_entry php_event_bevent_ce_functions[] = {/* {{{ */
-	PHP_ME(EventBufferEvent, __construct,       arginfo_bufferevent__construct,              ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, __construct,       arginfo_bufferevent__construct,              ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
+	PHP_ME(EventBufferEvent, free,              arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, ref,               arginfo_event__void,                         ZEND_ACC_PUBLIC)
 	PHP_ME(EventBufferEvent, connect,           arginfo_bufferevent_connect,                 ZEND_ACC_PUBLIC)
 	PHP_ME(EventBufferEvent, connectHost,       arginfo_bufferevent_socket_connect_hostname, ZEND_ACC_PUBLIC)
 	PHP_ME(EventBufferEvent, getDnsErrorString, arginfo_event__void,                         ZEND_ACC_PUBLIC)
 	PHP_ME(EventBuffer, unlock,        arginfo_event__void,         ZEND_ACC_PUBLIC)
 	PHP_ME(EventBuffer, enableLocking, arginfo_event__void,         ZEND_ACC_PUBLIC)
 	PHP_ME(EventBuffer, add,           arginfo_evbuffer_add,        ZEND_ACC_PUBLIC)
-	PHP_ME(EventBuffer, addBuffer,     arginfo_evbuffer_add_buffer, ZEND_ACC_PUBLIC)
+	PHP_ME(EventBuffer, addBuffer,     arginfo_evbuffer_add_buffer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
 	PHP_ME(EventBuffer, remove,        arginfo_evbuffer_remove,     ZEND_ACC_PUBLIC)
 
 	PHP_FE_END
 #endif
 
 PHP_METHOD(EventBufferEvent, __construct);
+PHP_METHOD(EventBufferEvent, free);
+PHP_METHOD(EventBufferEvent, ref);
 PHP_METHOD(EventBufferEvent, createPair);
 PHP_METHOD(EventBufferEvent, connect);
 PHP_METHOD(EventBufferEvent, connectHost);

File src/structs.h

 	PHP_EVENT_OBJECT_HEAD;
 
 	struct event_base *base;
+	zend_bool          internal;   /* Whether is an internal pointer, e.g. obtained with evconnlistener_get_base() */
 } php_event_base_t;
 
 /* Represents EventConfig object */
 	struct evconnlistener *listener;
 	int                    stream_id;   /* Resource ID of the file descriptor. -1 if none */
 	zval                  *self;        /* Object itself. For callbacks              */
-	zval                  *base;        /* Event base associated with the listener   */
 	zval                  *data;        /* User custom data passed to callback       */
 
 	/* Accept callback */