1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2021 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Ruslan Osmanov <osmanov@php.net>                             |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "php_ev.h"
20 
21 /* Defined in ev.c */
22 extern zend_class_entry *ev_loop_class_entry_ptr;
23 
24 /* {{{ php_ev_watcher_callback() */
php_ev_watcher_callback(EV_P_ ev_watcher * watcher,int revents)25 void php_ev_watcher_callback(EV_P_ ev_watcher *watcher, int revents)
26 {
27 	zval            **args[2];
28 	zval             *key2;
29 	zval             *retval_ptr;
30 	zval             *self       = php_ev_watcher_self(watcher);
31 	zend_fcall_info  *pfci       = php_ev_watcher_fci(watcher);
32 
33 	TSRMLS_FETCH_FROM_CTX(php_ev_watcher_thread_ctx(watcher));
34 
35 	/* libev might have stopped watcher */
36 	if (php_ev_watcher_flags(watcher) & PHP_EV_WATCHER_FLAG_UNREFED
37 			&& !ev_is_active(watcher)) {
38 		PHP_EV_WATCHER_REF(watcher);
39 	}
40 
41 	if (UNEXPECTED(revents & EV_ERROR)) {
42 		int errorno = errno;
43 		php_error_docref(NULL TSRMLS_CC, E_WARNING,
44 				"Got unspecified libev error in revents, errno = %d, err = %s", errorno, strerror(errorno));
45 
46 		PHP_EV_EXIT_LOOP(EV_A);
47 	} else if (EXPECTED(ZEND_FCI_INITIALIZED(*pfci))) {
48 		ev_loop *loop = php_ev_watcher_loop(watcher)->loop;
49 
50 		/* Setup callback args */
51 		args[0] = &self;
52 		Z_ADDREF_P(self);
53 
54 		MAKE_STD_ZVAL(key2);
55 		args[1] = &key2;
56 		ZVAL_LONG(key2, revents);
57 
58 		/* Prepare callback */
59 		zend_uint fcc_param_count = php_ev_watcher_fcc(watcher)->function_handler ?
60 			php_ev_watcher_fcc(watcher)->function_handler->common.num_args : 0;
61 		pfci->params         = args;
62 		pfci->retval_ptr_ptr = &retval_ptr;
63 		pfci->param_count    = MIN(2, fcc_param_count);
64 		pfci->no_separation  = 1;
65 
66 		if (EXPECTED(zend_call_function(pfci, php_ev_watcher_fcc(watcher) TSRMLS_CC) == SUCCESS
67 		        && retval_ptr)) {
68 		    zval_ptr_dtor(&retval_ptr);
69 		} else {
70 		    php_error_docref(NULL TSRMLS_CC, E_WARNING,
71 		            "An error occurred while invoking the callback");
72 		}
73 
74 		if (EG(exception)) {
75 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Stopping event loop because of uncaught exception in the callback");
76 			PHP_EV_ASSERT(loop);
77 			ev_break(loop, EVBREAK_ONE);
78 		}
79 
80 		zval_ptr_dtor(&self);
81 		zval_ptr_dtor(&key2);
82 	}
83 }
84 /* }}} */
85 
86 /* {{{ php_ev_set_watcher()
87  * Configure preallocated watcher of the specified type, initialize common watcher fields
88  */
php_ev_set_watcher(ev_watcher * w,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)89 void php_ev_set_watcher(ev_watcher *w, 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)
90 {
91 	/* Re-link the doubly linked list */
92 
93 	ev_watcher *w_next = o_loop->w;
94 	o_loop->w          = w;
95 
96 	if (w_next) {
97 		php_ev_watcher_next(w)      = (void *) w_next;
98 		php_ev_watcher_prev(w_next) = (void *) w;
99 	}
100 
101 	ev_init((ev_watcher *) w, (ZEND_FCI_INITIALIZED(*pfci) ? php_ev_watcher_callback : 0));
102 
103 	if (data) {
104 		Z_ADDREF_P(data);
105 	}
106 
107 #if 0
108 	Z_ADDREF_P(self);
109 #endif
110 
111 	php_ev_watcher_self(w)  = self;
112 	php_ev_watcher_data(w)  = data;
113 	php_ev_watcher_loop(w)  = o_loop;
114 	php_ev_watcher_flags(w) = PHP_EV_WATCHER_FLAG_KEEP_ALIVE /*| PHP_EV_WATCHER_FLAG_SELF_UNREFED*/;
115 
116 	PHP_EV_COPY_FCALL_INFO(php_ev_watcher_fci(w), php_ev_watcher_fcc(w), pfci, pfcc);
117 
118 	php_ev_set_watcher_priority(w, priority);
119 
120 	TSRMLS_SET_CTX(php_ev_watcher_thread_ctx(w));
121 }
122 /* }}} */
123 
124 /* {{{ php_ev_new_watcher()
125  * Create watcher of the specified type, initialize common watcher fields
126  */
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)127 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)
128 {
129 	void *w = ecalloc(1, size);
130 
131 	php_ev_set_watcher((ev_watcher *) w, size, self, o_loop, pfci, pfcc, data, priority TSRMLS_CC);
132 
133 	return w;
134 }
135 /* }}} */
136 
137 /* {{{ php_ev_start_watcher() */
php_ev_start_watcher(ev_watcher * watcher TSRMLS_DC)138 void php_ev_start_watcher(ev_watcher *watcher TSRMLS_DC)
139 {
140 	switch (watcher->type) {
141 		case EV_IO:
142 			PHP_EV_WATCHER_START(ev_io, watcher);
143 			break;
144 		case EV_TIMER:
145 			PHP_EV_WATCHER_START(ev_timer, watcher);
146 			break;
147 #if EV_PERIODIC_ENABLE
148 		case EV_PERIODIC:
149 			PHP_EV_WATCHER_START(ev_periodic, watcher);
150 			break;
151 #endif
152 #if EV_SIGNAL_ENABLE
153 		case EV_SIGNAL:
154 			PHP_EV_SIGNAL_START((ev_signal *) watcher);
155 			break;
156 #endif
157 #if EV_CHILD_ENABLE
158 		case EV_CHILD:
159 			PHP_EV_WATCHER_START(ev_child, watcher);
160 			break;
161 #endif
162 #if EV_STAT_ENABLE
163 		case EV_STAT:
164 			PHP_EV_WATCHER_START(ev_stat, watcher);
165 			break;
166 #endif
167 #if EV_IDLE_ENABLE
168 		case EV_IDLE:
169 			PHP_EV_WATCHER_START(ev_idle, watcher);
170 			break;
171 #endif
172 #if EV_PREPARE_ENABLE
173 		case EV_PREPARE:
174 			PHP_EV_WATCHER_START(ev_prepare, watcher);
175 			break;
176 #endif
177 #if EV_CHECK_ENABLE
178 		case EV_CHECK:
179 			PHP_EV_WATCHER_START(ev_check, watcher);
180 			break;
181 #endif
182 #if EV_EMBED_ENABLE
183 		case EV_EMBED:
184 			PHP_EV_WATCHER_START(ev_embed, watcher);
185 			break;
186 #endif
187 #if EV_FORK_ENABLE
188 		case EV_FORK:
189 			PHP_EV_WATCHER_START(ev_fork, watcher);
190 			break;
191 #endif
192 #if EV_ASYNC_ENABLE
193 		case EV_ASYNC:
194 			PHP_EV_WATCHER_START(ev_async, watcher);
195 			break;
196 #endif
197 		default:
198 			break;
199 	}
200 }
201 /* }}} */
202 
203 /* {{{ php_ev_stop_watcher() */
php_ev_stop_watcher(ev_watcher * watcher TSRMLS_DC)204 void php_ev_stop_watcher(ev_watcher *watcher TSRMLS_DC)
205 {
206 	switch (php_ev_watcher_type(watcher)) {
207 		case EV_IO:
208 			PHP_EV_WATCHER_STOP(ev_io, watcher);
209 			break;
210 		case EV_TIMER:
211 			PHP_EV_WATCHER_STOP(ev_timer, watcher);
212 			break;
213 #if EV_PERIODIC_ENABLE
214 		case EV_PERIODIC:
215 			PHP_EV_WATCHER_STOP(ev_periodic, watcher);
216 			break;
217 #endif
218 #if EV_SIGNAL_ENABLE
219 		case EV_SIGNAL:
220 			PHP_EV_SIGNAL_STOP((ev_signal *) watcher);
221 			break;
222 #endif
223 #if EV_CHILD_ENABLE
224 		case EV_CHILD:
225 			PHP_EV_WATCHER_STOP(ev_child, watcher);
226 			break;
227 #endif
228 #if EV_STAT_ENABLE
229 		case EV_STAT:
230 			PHP_EV_WATCHER_STOP(ev_stat, watcher);
231 			break;
232 #endif
233 #if EV_IDLE_ENABLE
234 		case EV_IDLE:
235 			PHP_EV_WATCHER_STOP(ev_idle, watcher);
236 			break;
237 #endif
238 #if EV_PREPARE_ENABLE
239 		case EV_PREPARE:
240 			PHP_EV_WATCHER_STOP(ev_prepare, watcher);
241 			break;
242 #endif
243 #if EV_CHECK_ENABLE
244 		case EV_CHECK:
245 			PHP_EV_WATCHER_STOP(ev_check, watcher);
246 			break;
247 #endif
248 #if EV_EMBED_ENABLE
249 		case EV_EMBED:
250 			PHP_EV_WATCHER_STOP(ev_embed, watcher);
251 			break;
252 #endif
253 #if EV_FORK_ENABLE
254 		case EV_FORK:
255 			PHP_EV_WATCHER_STOP(ev_fork, watcher);
256 			break;
257 #endif
258 #if EV_ASYNC_ENABLE
259 		case EV_ASYNC:
260 			PHP_EV_WATCHER_STOP(ev_async, watcher);
261 			break;
262 #endif
263 		default:
264 			break;
265 	}
266 }
267 /* }}} */
268 
269 
270 /* {{{ Methods */
271 
272 /* {{{ proto void EvWatcher::start(void) */
PHP_METHOD(EvWatcher,start)273 PHP_METHOD(EvWatcher, start)
274 {
275 	php_ev_object *o_self;
276 
277 	if (zend_parse_parameters_none() == FAILURE) {
278 		return;
279 	}
280 
281 	o_self = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
282 
283 	php_ev_start_watcher(PHP_EV_WATCHER_FETCH_FROM_OBJECT(o_self) TSRMLS_CC);
284 }
285 /* }}} */
286 
287 /* {{{ proto void EvWatcher::stop(void) */
PHP_METHOD(EvWatcher,stop)288 PHP_METHOD(EvWatcher, stop)
289 {
290 	php_ev_object *o_self;
291 
292 	if (zend_parse_parameters_none() == FAILURE) {
293 		return;
294 	}
295 
296 	o_self = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
297 
298 	php_ev_stop_watcher(PHP_EV_WATCHER_FETCH_FROM_OBJECT(o_self) TSRMLS_CC);
299 }
300 /* }}} */
301 
302 /* {{{ proto int EvWatcher::clear(void) */
PHP_METHOD(EvWatcher,clear)303 PHP_METHOD(EvWatcher, clear)
304 {
305 	php_ev_object *o_self;
306 	ev_watcher    *w;
307 
308 	if (zend_parse_parameters_none() == FAILURE) {
309 		return;
310 	}
311 
312 	o_self = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
313 	w      = PHP_EV_WATCHER_FETCH_FROM_OBJECT(o_self);
314 
315 	RETURN_LONG((long)ev_clear_pending(php_ev_watcher_loop_ptr(w), w));
316 }
317 /* }}} */
318 
319 /* {{{ proto void EvWatcher::invoke(int revents) */
PHP_METHOD(EvWatcher,invoke)320 PHP_METHOD(EvWatcher, invoke)
321 {
322 	php_ev_object *o_self;
323 	ev_watcher    *w;
324 	long           revents;
325 
326 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
327 				&revents) == FAILURE) {
328 		return;
329 	}
330 
331 	o_self = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
332 	w      = PHP_EV_WATCHER_FETCH_FROM_OBJECT(o_self);
333 
334 	ev_invoke(php_ev_watcher_loop_ptr(w), w, (int)revents);
335 }
336 /* }}} */
337 
338 /* {{{ proto void EvWatcher::feed(int revents) */
PHP_METHOD(EvWatcher,feed)339 PHP_METHOD(EvWatcher, feed)
340 {
341 	php_ev_object *o_self;
342 	ev_watcher    *w;
343 	long           revents;
344 
345 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
346 				&revents) == FAILURE) {
347 		return;
348 	}
349 
350 	o_self = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
351 	w      = PHP_EV_WATCHER_FETCH_FROM_OBJECT(o_self);
352 
353 	ev_feed_event(php_ev_watcher_loop_ptr(w), w, (int)revents);
354 }
355 /* }}} */
356 
357 /* {{{ proto EvLoop EvWatcher::getLoop(void) */
PHP_METHOD(EvWatcher,getLoop)358 PHP_METHOD(EvWatcher, getLoop)
359 {
360 	php_ev_object *o_self;
361 	php_ev_loop   *o_loop;
362 	ev_watcher    *w;
363 
364 	if (zend_parse_parameters_none() == FAILURE) {
365 		return;
366 	}
367 
368 	o_self = (php_ev_object *) zend_object_store_get_object(getThis() TSRMLS_CC);
369 	w      = PHP_EV_WATCHER_FETCH_FROM_OBJECT(o_self);
370 	o_loop = php_ev_watcher_loop(w);
371 
372 	zval *zloop = (zval *) ev_userdata(o_loop->loop);
373 
374     if (!zloop) {
375     	RETURN_NULL();
376     }
377 	RETVAL_ZVAL(zloop, 1, 0);
378 }
379 /* }}} */
380 
381 /* {{{ proto int EvWatcher::keepalive([bool value = TRUE]) */
PHP_METHOD(EvWatcher,keepalive)382 PHP_METHOD(EvWatcher, keepalive)
383 {
384 	ev_watcher *w;
385 	zend_bool n_value = TRUE;
386 
387 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &n_value) == FAILURE) {
388 		return;
389 	}
390 
391 	w = PHP_EV_WATCHER_FETCH_FROM_THIS();
392 
393 	/* Returning previous state */
394 	RETVAL_BOOL((php_ev_watcher_flags(w) & PHP_EV_WATCHER_FLAG_KEEP_ALIVE));
395 	n_value = n_value != FALSE ? PHP_EV_WATCHER_FLAG_KEEP_ALIVE : 0;
396 
397 	/* Update watcher flags, if keepalive flag changed */
398 	if ((n_value ^ php_ev_watcher_flags(w)) & PHP_EV_WATCHER_FLAG_KEEP_ALIVE) {
399 		php_ev_watcher_flags(w) = (php_ev_watcher_flags(w) & ~PHP_EV_WATCHER_FLAG_KEEP_ALIVE) | n_value;
400 		PHP_EV_WATCHER_REF(w);
401 		PHP_EV_WATCHER_UNREF(w);
402 	}
403 }
404 /* }}} */
405 
406 /* {{{ proto void EvWatcher::setCallback(callable callback) */
PHP_METHOD(EvWatcher,setCallback)407 PHP_METHOD(EvWatcher, setCallback)
408 {
409 	ev_watcher            *w;
410 	zend_fcall_info        fci;
411 	zend_fcall_info_cache  fcc;
412 
413 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f",
414 				&fci, &fcc) == FAILURE) {
415 		return;
416 	}
417 
418 	w = PHP_EV_WATCHER_FETCH_FROM_THIS();
419 
420 	PHP_EV_FREE_FCALL_INFO(w->fci, w->fcc);
421 	PHP_EV_COPY_FCALL_INFO(w->fci, w->fcc, &fci, &fcc);
422 }
423 /* }}} */
424 
425 /* Methods }}} */
426 
427 
428 /*
429  * Local variables:
430  * tab-width: 4
431  * c-basic-offset: 4
432  * End:
433  * vim600: noet sw=4 ts=4 sts=4 fdm=marker
434  * vim<600: noet sw=4 ts=4 sts=4
435  */
436