Ruslan Osmanov avatar Ruslan Osmanov committed fed3901

Add: EvPeriodic class rescheduler mode support

Comments (0)

Files changed (7)

TODO

-Problem: move the class code separate files, then simply
-========================================================
-
-#include "loop.c"
-#include "watcher.c"
-#include "io.c"
-#include "timer.c"
-#if EV_PERIODIC_ENABLE
-#include "periodic.c"
-#endif
-#if EV_PREPARE_ENABLE
-#include "scheduler.c"
-#endif
-#endif
-#if EV_SIGNAL_ENABLE
-#include "signal.c"
-#endif
-#if EV_CHILD_ENABLE
-#include "child.c"
-#endif
-#if EV_STAT_ENABLE
-#include "stat.c"
-#endif
-#if EV_IDLE_ENABLE
-#include "idle.c"
-#endif
-#if EV_PREPARE_ENABLE
-#include "prepare.c"
-#endif
-#if EV_CHECK_ENABLE
-#include "check.c"
-#endif
-#if EV_EMBED_ENABLE
-#include "embed.c"
-#endif
-#if EV_FORK_ENABLE
-#include "fork.c"
-#endif
-#if EV_ASYNC_ENABLE
-#include "async.c"
-#endif
-
-Somewhere in ev.c, or php_ev.h
-
-========================================================
-C++? 
-========================================================
-
-Manipulations with similar watcher types, and the watcher type inheritance
-might be done with C++ classes.  To wrap a C++ class is not difficult. To
-compile libev with CXX is not a problem either.  But do I really need this?
-
-I'll try to cope with pure C, then try to comlipe with C++.  It is also
-possible to compile just some files with CXX, and some with the good old C.
-intl extention has a cpp file and the rest are c and h ones, for instance.
 
 #include "libev/ev.h"
 
+
+/* 
+ * TODO: consider refactoring of embed.h and types.h.
+ * We can't declare this above #include "libev/ev.h", since we'll get
+ * `field 'periodic' has incomplete type' compilation error.
+ *
+ * php_ev_periodic is special type for periodic watcher.
+ * I.e. we don't want to embed extra members into EV_COMMON
+ * Extends ev_watcher
+ */
+
+typedef struct php_ev_periodic {
+	struct ev_periodic     periodic;     /* Contains common watcher vars embedded         */
+	zend_fcall_info       *fci;   /* fci/fcc store specific "rescheduler" callback */
+	zend_fcall_info_cache *fcc;
+} php_ev_periodic;
+
 #endif /* PHP_EV_EMBED_H */
 /*
  * Local variables:
 	php_ev_object *obj_ptr = (php_ev_object *) object;
 
 	PHP_EV_ASSERT(obj_ptr->ptr);
-	ev_periodic *ptr = (ev_periodic *) obj_ptr->ptr;
+	ev_periodic *ptr              = (ev_periodic *) obj_ptr->ptr;
+	php_ev_periodic *periodic_ptr = (php_ev_periodic *) obj_ptr->ptr;
+
+	PHP_EV_FREE_FCALL_INFO(periodic_ptr->fci, periodic_ptr->fcc);
 
 	/* Free base class members */
 	php_ev_watcher_free_storage((ev_watcher *) ptr TSRMLS_CC);
 */
 #include "watcher.h"
 
-/* {{{ proto EvPeriodic::__construct(double offset, double interval, EvLoop loop, callable callback[, mixed data = NULL[, int priority = 0]]) */
+/* {{{ php_ev_periodic_rescheduler */
+static ev_tstamp php_ev_periodic_rescheduler(ev_periodic *w, ev_tstamp now)
+{
+	ev_tstamp         retval;
+	zval             *self;
+	php_ev_object    *ev_obj;
+	php_ev_periodic  *periodic_ptr;
+	zend_fcall_info  *pfci;
+
+	TSRMLS_FETCH_FROM_CTX(php_ev_watcher_thread_ctx(w));
+
+	/* Fetch php_ev_periodic to access rescheduler's fci and fcc */
+
+	self   = php_ev_watcher_self(w);
+	ev_obj = (php_ev_object *) zend_object_store_get_object(self TSRMLS_CC);
+
+    if (!ev_obj->ptr) {
+        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Watcher is not initialized");
+        return now;
+    }
+
+	periodic_ptr = (php_ev_periodic *) ev_obj->ptr;
+	pfci         = periodic_ptr->fci;
+
+	if (pfci && ZEND_FCI_INITIALIZED(*pfci)) {
+		zval            **args[2];
+		zval             *key1;
+		zval             *key2;
+		zval             *retval_ptr;
+
+		/* Setup callback args */
+		key1 = self;
+		args[0] = &key1;
+		zval_add_ref(&key1);
+
+		MAKE_STD_ZVAL(key2);
+		args[1] = &key2;
+		ZVAL_DOUBLE(key2, (double) now);
+
+		/* Prepare callback */
+		pfci->params         = args;
+		pfci->retval_ptr_ptr = &retval_ptr;
+		pfci->param_count    = 2;
+		pfci->no_separation  = 0;
+
+		if (zend_call_function(pfci, periodic_ptr->fcc TSRMLS_CC) == SUCCESS
+		        && retval_ptr) {
+		    retval = (ev_tstamp) Z_DVAL_P(retval_ptr);
+		    if (retval < now)
+		    	retval = now;
+
+		    zval_ptr_dtor(&retval_ptr);
+		} else {
+			retval = now;
+		    php_error_docref(NULL TSRMLS_CC, E_WARNING,
+		            "An error occurred while invoking rescheduler callback");
+		}
+
+		zval_ptr_dtor(&key1);
+		zval_ptr_dtor(&key2);
+	} else {
+		retval = now;
+	}
+
+	return retval;
+}
+/* }}} */
+
+
+/* {{{ proto EvPeriodic::__construct(double offset, double interval, callable reschedule_cb, EvLoop loop, callable callback[, mixed data = NULL[, int priority = 0]]) 
+ * NOTE: reschedule_cb could be NULL */
 PHP_METHOD(EvPeriodic, __construct)
 {
-	double         offset;
-	double         interval;
-	zval          *self;
-	php_ev_object *o_self;
-	php_ev_object *o_loop;
-	ev_periodic   *periodic_watcher;
+	double                 offset;
+	double                 interval;
+	zend_fcall_info        fci_reschedule;
+	zend_fcall_info_cache  fcc_reschedule;
 
-	zval                  *loop;
-	zval                  *data       = NULL;
-	zend_fcall_info        fci        = empty_fcall_info;
-	zend_fcall_info_cache  fcc        = empty_fcall_info_cache;
-	long                   priority   = 0;
+	zval                  *self;
+	php_ev_object         *o_self;
+	php_ev_object         *o_loop;
+	ev_periodic           *periodic_watcher;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ddOf|z!l",
-				&offset, &interval, &loop, ev_loop_class_entry_ptr, &fci, &fcc,
+	zval                  *loop;
+	zval                  *data             = NULL;
+	zend_fcall_info        fci              = empty_fcall_info;
+	zend_fcall_info_cache  fcc              = empty_fcall_info_cache;
+	long                   priority         = 0;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ddf!Of|z!l",
+				&offset, &interval, &fci_reschedule, &fcc_reschedule,
+				&loop, ev_loop_class_entry_ptr, &fci, &fcc,
 				&data, &priority) == FAILURE) {
 		return;
 	}
 
 	PHP_EV_CHECK_REPEAT(interval);
 
+	php_ev_periodic *periodic_ptr = (php_ev_periodic *) emalloc(sizeof(php_ev_periodic));
+	memset(periodic_ptr, 0, sizeof(php_ev_periodic));
+
+	periodic_watcher = &periodic_ptr->periodic;
+
 	self             = getThis();
 	o_self           = (php_ev_object *) zend_object_store_get_object(self TSRMLS_CC);
 	o_loop           = (php_ev_object *) zend_object_store_get_object(loop TSRMLS_CC);
-	periodic_watcher = (ev_periodic *) php_ev_new_watcher(sizeof(ev_periodic), self,
+
+	php_ev_set_watcher((ev_watcher *)periodic_watcher, sizeof(ev_periodic), self,
 			PHP_EV_LOOP_OBJECT_FETCH_FROM_OBJECT(o_loop),
 			&fci, &fcc, data, priority TSRMLS_CC);
 
 	periodic_watcher->type = EV_PERIODIC;
-	
-	php_printf("ev_periodic_set(%f, %f)\n", offset, interval);
-	ev_periodic_set(periodic_watcher, offset, interval, 0);
 
-	o_self->ptr = (void *) periodic_watcher;
+
+	if (ZEND_FCI_INITIALIZED(fci_reschedule)) { /* argument is not NULL */
+		PHP_EV_COPY_FCALL_INFO(periodic_ptr->fci, periodic_ptr->fcc,
+				&fci_reschedule, &fcc_reschedule);
+
+		ev_periodic_set(periodic_watcher, offset, interval, php_ev_periodic_rescheduler);
+	} else {
+		ev_periodic_set(periodic_watcher, offset, interval, 0);
+	}
+
+	o_self->ptr     = (void *) periodic_ptr;
 }
 /* }}} */
 
-/* {{{ proto void EvPeriodic::set(double offset, double interval) */
+/* {{{ proto void EvPeriodic::set(double offset, double interval[, callable reschedule_cb]) */
 PHP_METHOD(EvPeriodic, set)
 {
-	double       offset;
-	double       interval;
-	ev_periodic *periodic_watcher;
+	double                offset;
+	double                interval;
+	zend_fcall_info       fci      = empty_fcall_info;
+	zend_fcall_info_cache fcc      = empty_fcall_info_cache;
+
+	ev_periodic     *periodic_watcher;
+	php_ev_object   *ev_obj;
+	php_ev_periodic *periodic_ptr;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd",
-				&offset, &interval) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd|f",
+				&offset, &interval, &fci, &fcc) == FAILURE) {
 		return;
 	}
 
 	PHP_EV_CHECK_REPEAT(interval);
 
-	periodic_watcher = (ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_THIS();
+	ev_obj           = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
+	periodic_watcher = (ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(ev_obj);
+	periodic_ptr     = (php_ev_periodic *) PHP_EV_WATCHER_FETCH_FROM_OBJECT(ev_obj);
+	
+	/* Free fci and fcc within periodic_ptr, since they will be overwritten anyways */
+
+	if (ZEND_FCI_INITIALIZED(*periodic_ptr->fci)) {
+		PHP_EV_FREE_FCALL_INFO(periodic_ptr->fci, periodic_ptr->fcc);
+	}
 
-	PHP_EV_WATCHER_RESET(ev_periodic, periodic_watcher, (periodic_watcher, offset, interval, 0));
+	/* Reconfigure reschedule_cb */
+
+	if (ZEND_FCI_INITIALIZED(fci)) {
+		PHP_EV_COPY_FCALL_INFO(periodic_ptr->fci, periodic_ptr->fcc, &fci, &fcc);
+
+		PHP_EV_WATCHER_RESET(ev_periodic, periodic_watcher,
+				(periodic_watcher, offset, interval, php_ev_periodic_rescheduler));
+	} else {
+		PHP_EV_WATCHER_RESET(ev_periodic, periodic_watcher,
+				(periodic_watcher, offset, interval, 0));
+	}
 }
 /* }}} */
 
 typedef struct php_ev_object {
 	zend_object  zo;
 	HashTable   *prop_handler;
-	void        *ptr; /* Pointer to ev_watcher or php_ev_loop */
+	void        *ptr; /* Pointer to ev_watcher, php_ev_loop or php_ev_periodic */
 } php_ev_object;
 
 /* php_ev_loop pointer is stored in php_ev_object.ptr struct member */
 	struct ev_watcher     *w;                          /* Head of linked list                                      */
 } php_ev_loop;
 
+#if 0
+/* php_ev_periodic is special type for periodic watcher.
+ * I.e. we don't want to embed extra members into EV_COMMON
+ * Extends ev_watcher
+ */
+
+typedef struct php_ev_periodic {
+	struct ev_watcher      w;     /* Contains common watcher vars embedded         */
+	zend_fcall_info       *fci;   /* fci/fcc store specific "rescheduler" callback */
+	zend_fcall_info_cache *fcc;
+} php_ev_periodic;
+#endif
+
 /* Property handlers */
 
 typedef int (*php_ev_read_t)(php_ev_object  *obj, zval **retval TSRMLS_DC);
 }
 /* }}} */
 
-/* {{{ php_ev_new_watcher()
- * Create watcher of the specified type, initialize common watcher fields
+/* {{{ php_ev_set_watcher()
+ * Configure preallocated watcher of the specified type, initialize common watcher fields
  */
-void *php_ev_new_watcher(size_t size, zval *self, php_ev_loop *loop, const zend_fcall_info *pfci, const zend_fcall_info_cache *pfcc, zval *data, int priority TSRMLS_DC)
+void php_ev_set_watcher(ev_watcher *w, size_t size, zval *self, php_ev_loop *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);
-
 	ev_watcher *w_prev = loop->w;
-	loop->w = (ev_watcher *) w;	
+	loop->w = w;
 
 	if (w_prev) {
 		php_ev_watcher_prev(w) = (void *) w_prev;
 
 	PHP_EV_COPY_FCALL_INFO(php_ev_watcher_fci(w), php_ev_watcher_fcc(w), pfci, pfcc);
 
-	php_ev_set_watcher_priority((ev_watcher *) w, priority TSRMLS_CC);
+	php_ev_set_watcher_priority(w, priority TSRMLS_CC);
 
 	TSRMLS_SET_CTX(php_ev_watcher_thread_ctx(w));
-	
+}
+/* }}} */
+
+/* {{{ php_ev_new_watcher()
+ * Create watcher of the specified type, initialize common watcher fields
+ */
+void *php_ev_new_watcher(size_t size, zval *self, php_ev_loop *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);
+
+	php_ev_set_watcher((ev_watcher *)w, size, self, loop, pfci, pfcc, data, priority TSRMLS_CC);
+
 	return w;
 }
 /* }}} */
     } while(0)
 
 void php_ev_watcher_callback(EV_P_ ev_watcher *watcher, int revents);
+void php_ev_set_watcher(ev_watcher *w, size_t size, zval *self, php_ev_loop *loop,
+		const zend_fcall_info *pfci, const zend_fcall_info_cache *pfcc, zval *data, int priority TSRMLS_DC);
 void *php_ev_new_watcher(size_t size, zval *self, php_ev_loop *loop,
 		const zend_fcall_info *pfci, const zend_fcall_info_cache *pfcc, zval *data, int priority TSRMLS_DC);
 void php_ev_stop_watcher(ev_watcher *watcher TSRMLS_DC);
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.