1 /*
2  +----------------------------------------------------------------------+
3  | Swoole                                                               |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 2012-2015 The Swoole Group                             |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 2.0 of the Apache 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.apache.org/licenses/LICENSE-2.0.html                      |
11  | If you did not receive a copy of the Apache2.0 license and are unable|
12  | to obtain it through the world-wide-web, please send a note to       |
13  | license@swoole.com so we can mail you a copy immediately.            |
14  +----------------------------------------------------------------------+
15  | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
16  +----------------------------------------------------------------------+
17  */
18 
19 #include "php_swoole_cxx.h"
20 #include "swoole_server.h"
21 #include "swoole_signal.h"
22 
23 using namespace swoole;
24 using swoole::network::Socket;
25 
26 static std::unordered_map<int, Socket *> event_socket_map;
27 
28 zend_class_entry *swoole_event_ce;
29 static zend_object_handlers swoole_event_handlers;
30 
31 struct EventObject {
32     zval zsocket;
33     zend_fcall_info_cache fci_cache_read;
34     zend_fcall_info_cache fci_cache_write;
35 };
36 
37 static int event_readable_callback(Reactor *reactor, Event *event);
38 static int event_writable_callback(Reactor *reactor, Event *event);
39 static int event_error_callback(Reactor *reactor, Event *event);
40 static void event_defer_callback(void *data);
41 static void event_end_callback(void *data);
42 
43 SW_EXTERN_C_BEGIN
44 static PHP_FUNCTION(swoole_event_add);
45 static PHP_FUNCTION(swoole_event_set);
46 static PHP_FUNCTION(swoole_event_del);
47 static PHP_FUNCTION(swoole_event_write);
48 static PHP_FUNCTION(swoole_event_wait);
49 static PHP_FUNCTION(swoole_event_rshutdown);
50 static PHP_FUNCTION(swoole_event_exit);
51 static PHP_FUNCTION(swoole_event_defer);
52 static PHP_FUNCTION(swoole_event_cycle);
53 static PHP_FUNCTION(swoole_event_dispatch);
54 static PHP_FUNCTION(swoole_event_isset);
55 SW_EXTERN_C_END
56 
57 // clang-format off
58 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
59 ZEND_END_ARG_INFO()
60 
61 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_add, 0, 0, 2)
62     ZEND_ARG_INFO(0, fd)
63     ZEND_ARG_CALLABLE_INFO(0, read_callback, 1)
64     ZEND_ARG_CALLABLE_INFO(0, write_callback, 1)
65     ZEND_ARG_INFO(0, events)
66 ZEND_END_ARG_INFO()
67 
68 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_set, 0, 0, 1)
69     ZEND_ARG_INFO(0, fd)
70     ZEND_ARG_CALLABLE_INFO(0, read_callback, 1)
71     ZEND_ARG_CALLABLE_INFO(0, write_callback, 1)
72     ZEND_ARG_INFO(0, events)
73 ZEND_END_ARG_INFO()
74 
75 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_write, 0, 0, 2)
76     ZEND_ARG_INFO(0, fd)
77     ZEND_ARG_INFO(0, data)
78 ZEND_END_ARG_INFO()
79 
80 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_defer, 0, 0, 1)
81     ZEND_ARG_CALLABLE_INFO(0, callback, 0)
82 ZEND_END_ARG_INFO()
83 
84 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_cycle, 0, 0, 1)
85     ZEND_ARG_CALLABLE_INFO(0, callback, 1)
86     ZEND_ARG_INFO(0, before)
87 ZEND_END_ARG_INFO()
88 
89 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_del, 0, 0, 1)
90     ZEND_ARG_INFO(0, fd)
91 ZEND_END_ARG_INFO()
92 
93 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_event_isset, 0, 0, 1)
94     ZEND_ARG_INFO(0, fd)
95     ZEND_ARG_INFO(0, events)
96 ZEND_END_ARG_INFO()
97 
98 static const zend_function_entry swoole_event_methods[] =
99 {
100     ZEND_FENTRY(add, ZEND_FN(swoole_event_add), arginfo_swoole_event_add, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
101     ZEND_FENTRY(del, ZEND_FN(swoole_event_del), arginfo_swoole_event_del, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
102     ZEND_FENTRY(set, ZEND_FN(swoole_event_set), arginfo_swoole_event_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
103     ZEND_FENTRY(isset, ZEND_FN(swoole_event_isset), arginfo_swoole_event_isset, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
104     ZEND_FENTRY(dispatch, ZEND_FN(swoole_event_dispatch), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
105     ZEND_FENTRY(defer, ZEND_FN(swoole_event_defer), arginfo_swoole_event_defer, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
106     ZEND_FENTRY(cycle, ZEND_FN(swoole_event_cycle), arginfo_swoole_event_cycle, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
107     ZEND_FENTRY(write, ZEND_FN(swoole_event_write), arginfo_swoole_event_write, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
108     ZEND_FENTRY(wait, ZEND_FN(swoole_event_wait), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
109     ZEND_FENTRY(rshutdown, ZEND_FN(swoole_event_rshutdown), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
110     ZEND_FENTRY(exit, ZEND_FN(swoole_event_exit), arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
111     PHP_FE_END
112 };
113 // clang-format on
114 
php_swoole_event_minit(int module_number)115 void php_swoole_event_minit(int module_number) {
116     SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", "swoole_event", nullptr, swoole_event_methods);
117     SW_SET_CLASS_CREATE(swoole_event, sw_zend_create_object_deny);
118 
119     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "add", CG(function_table), "swoole_event_add");
120     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "del", CG(function_table), "swoole_event_del");
121     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "set", CG(function_table), "swoole_event_set");
122     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "isset", CG(function_table), "swoole_event_isset");
123     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "dispatch", CG(function_table), "swoole_event_dispatch");
124     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "defer", CG(function_table), "swoole_event_defer");
125     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "cycle", CG(function_table), "swoole_event_cycle");
126     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "write", CG(function_table), "swoole_event_write");
127     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "wait", CG(function_table), "swoole_event_wait");
128     SW_FUNCTION_ALIAS(&swoole_event_ce->function_table, "exit", CG(function_table), "swoole_event_exit");
129 }
130 
event_object_free(void * data)131 static void event_object_free(void *data) {
132     EventObject *peo = (EventObject *) data;
133     if (peo->fci_cache_read.function_handler) {
134         sw_zend_fci_cache_discard(&peo->fci_cache_read);
135     }
136     if (peo->fci_cache_write.function_handler) {
137         sw_zend_fci_cache_discard(&peo->fci_cache_write);
138     }
139     zval_ptr_dtor((&peo->zsocket));
140     efree(peo);
141 }
142 
event_readable_callback(Reactor * reactor,swEvent * event)143 static int event_readable_callback(Reactor *reactor, swEvent *event) {
144     EventObject *peo = (EventObject *) event->socket->object;
145 
146     zval argv[1];
147     argv[0] = peo->zsocket;
148 
149     if (UNEXPECTED(!zend::function::call(&peo->fci_cache_read, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) {
150         php_swoole_fatal_error(E_WARNING,
151                                "%s: onRead callback handler error, fd [%d] will be removed from reactor",
152                                ZSTR_VAL(swoole_event_ce->name),
153                                php_swoole_convert_to_fd(&peo->zsocket));
154         event->socket->object = nullptr;
155         swoole_event_defer(event_object_free, peo);
156         swoole_event_del(event->socket);
157         return SW_ERR;
158     }
159 
160     return SW_OK;
161 }
162 
event_writable_callback(Reactor * reactor,Event * event)163 static int event_writable_callback(Reactor *reactor, Event *event) {
164     EventObject *peo = (EventObject *) event->socket->object;
165 
166     zval argv[1];
167     argv[0] = peo->zsocket;
168 
169     if (UNEXPECTED(!zend::function::call(&peo->fci_cache_write, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) {
170         php_swoole_fatal_error(E_WARNING,
171                                "%s: onWrite callback handler error, fd [%d] will be removed from reactor",
172                                ZSTR_VAL(swoole_event_ce->name),
173                                php_swoole_convert_to_fd(&peo->zsocket));
174         event->socket->object = nullptr;
175         swoole_event_defer(event_object_free, peo);
176         swoole_event_del(event->socket);
177         return SW_ERR;
178     }
179 
180     return SW_OK;
181 }
182 
event_error_callback(Reactor * reactor,swEvent * event)183 static int event_error_callback(Reactor *reactor, swEvent *event) {
184     if (!(event->socket->events & SW_EVENT_ERROR)) {
185         if (event->socket->events & SW_EVENT_READ) {
186             return reactor->get_handler(SW_EVENT_READ, event->socket->fd_type)(reactor, event);
187         } else {
188             return reactor->get_handler(SW_EVENT_WRITE, event->socket->fd_type)(reactor, event);
189         }
190     }
191 
192     int error;
193     if (event->socket->get_option(SOL_SOCKET, SO_ERROR, &error) < 0) {
194         php_swoole_sys_error(E_WARNING, "swoole_event->onError[1]: getsockopt[sock=%d] failed", event->fd);
195     }
196 
197     if (error != 0) {
198         php_swoole_fatal_error(
199             E_WARNING, "swoole_event->onError[1]: socket error. Error: %s [%d]", strerror(error), error);
200     }
201 
202     event_object_free(event->socket->object);
203     swoole_event_del(event->socket);
204 
205     return SW_OK;
206 }
207 
event_defer_callback(void * data)208 static void event_defer_callback(void *data) {
209     zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data;
210 
211     if (UNEXPECTED(!zend::function::call(fci_cache, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) {
212         php_swoole_error(E_WARNING, "%s::defer callback handler error", ZSTR_VAL(swoole_event_ce->name));
213     }
214 
215     sw_zend_fci_cache_discard(fci_cache);
216     efree(fci_cache);
217 }
218 
event_end_callback(void * data)219 static void event_end_callback(void *data) {
220     zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data;
221     if (UNEXPECTED(!zend::function::call(fci_cache, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) {
222         php_swoole_error(E_WARNING, "%s::end callback handler error", ZSTR_VAL(swoole_event_ce->name));
223     }
224 }
225 
php_swoole_reactor_init()226 int php_swoole_reactor_init() {
227     if (!SWOOLE_G(cli)) {
228         php_swoole_fatal_error(E_ERROR, "async-io must be used in PHP CLI mode");
229         return SW_ERR;
230     }
231 
232     if (sw_server()) {
233         if (sw_server()->is_task_worker() && !sw_server()->task_enable_coroutine) {
234             php_swoole_fatal_error(
235                 E_ERROR, "Unable to use async-io in task processes, please set `task_enable_coroutine` to true");
236             return SW_ERR;
237         }
238         if (sw_server()->is_manager()) {
239             php_swoole_fatal_error(E_ERROR, "Unable to use async-io in manager process");
240             return SW_ERR;
241         }
242     }
243     if (!sw_reactor()) {
244         swoole_trace_log(SW_TRACE_PHP, "init reactor");
245 
246         if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) {
247             php_swoole_fatal_error(E_ERROR, "Unable to create event-loop reactor");
248             return SW_ERR;
249         }
250 
251         php_swoole_register_shutdown_function("Swoole\\Event::rshutdown");
252     }
253 
254     if (sw_reactor() && SwooleG.user_exit_condition &&
255         !sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT)) {
256         sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, SwooleG.user_exit_condition);
257     }
258 
259     return SW_OK;
260 }
261 
php_swoole_event_wait()262 void php_swoole_event_wait() {
263     if (php_swoole_is_fatal_error() || !sw_reactor()) {
264         return;
265     }
266 #ifdef HAVE_SIGNALFD
267     if (sw_reactor()->check_signalfd) {
268         swoole_signalfd_setup(sw_reactor());
269     }
270 #endif
271     if (!sw_reactor()->if_exit() && !sw_reactor()->bailout) {
272         // Don't disable object slot reuse while running shutdown functions:
273         // https://github.com/php/php-src/commit/bd6eabd6591ae5a7c9ad75dfbe7cc575fa907eac
274 #if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE)
275         zend_bool in_shutdown = EG(flags) & EG_FLAGS_IN_SHUTDOWN;
276         EG(flags) &= ~EG_FLAGS_IN_SHUTDOWN;
277 #endif
278         if (sw_reactor()->wait(nullptr) < 0) {
279             php_swoole_sys_error(E_ERROR, "reactor wait failed");
280         }
281 #if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE)
282         if (in_shutdown) {
283             EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
284         }
285 #endif
286     }
287     swoole_event_free();
288 }
289 
php_swoole_event_exit()290 void php_swoole_event_exit() {
291     if (sw_reactor()) {
292         php_swoole_timer_clear_all();
293         sw_reactor()->running = false;
294     }
295 }
296 
php_swoole_convert_to_fd(zval * zsocket)297 int php_swoole_convert_to_fd(zval *zsocket) {
298     int fd = -1;
299 
300     switch (Z_TYPE_P(zsocket)) {
301     case IS_RESOURCE: {
302         php_stream *stream;
303         if ((php_stream_from_zval_no_verify(stream, zsocket))) {
304             if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) ==
305                     SUCCESS &&
306                 fd >= 0) {
307                 return fd;
308             }
309         }
310 #ifdef SWOOLE_SOCKETS_SUPPORT
311         else {
312             php_socket *php_sock;
313             if ((php_sock = SW_Z_SOCKET_P(zsocket))) {
314                 fd = php_sock->bsd_socket;
315                 return fd;
316             }
317         }
318 #endif
319         php_swoole_fatal_error(E_WARNING, "fd argument must be either valid PHP stream or valid PHP socket resource");
320         return SW_ERR;
321     }
322     case IS_LONG: {
323         fd = Z_LVAL_P(zsocket);
324         if (fd < 0) {
325             php_swoole_fatal_error(E_WARNING, "invalid file descriptor#%d passed", fd);
326             return SW_ERR;
327         }
328         return fd;
329     }
330     case IS_OBJECT: {
331         zval *zfd = nullptr;
332         if (instanceof_function(Z_OBJCE_P(zsocket), swoole_socket_coro_ce)) {
333             zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_FD), 0);
334         } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_client_ce)) {
335             zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_SOCK), 0);
336         } else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_process_ce)) {
337             zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPE), 0);
338         }
339         if (zfd == nullptr || Z_TYPE_P(zfd) != IS_LONG) {
340             return SW_ERR;
341         }
342         return Z_LVAL_P(zfd);
343     }
344     default:
345         php_swoole_fatal_error(E_WARNING, "invalid file descriptor passed");
346         return SW_ERR;
347     }
348 }
349 
php_swoole_convert_to_fd_ex(zval * zsocket,int * async)350 int php_swoole_convert_to_fd_ex(zval *zsocket, int *async) {
351     int fd;
352 
353     *async = 0;
354     if (Z_TYPE_P(zsocket) == IS_RESOURCE) {
355         php_stream *stream;
356         if ((php_stream_from_zval_no_verify(stream, zsocket))) {
357             if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) ==
358                     SUCCESS &&
359                 fd >= 0) {
360                 *async = (stream->wrapper && (stream->wrapper->wops == php_plain_files_wrapper.wops)) ? 0 : 1;
361                 return fd;
362             }
363         }
364 #ifdef SWOOLE_SOCKETS_SUPPORT
365         else {
366             php_socket *php_sock;
367             if ((php_sock = SW_Z_SOCKET_P(zsocket))) {
368                 fd = php_sock->bsd_socket;
369                 *async = 1;
370                 return fd;
371             }
372         }
373 #endif
374     }
375     php_swoole_fatal_error(E_WARNING, "fd argument must be either valid PHP stream or valid PHP socket resource");
376     return SW_ERR;
377 }
378 
379 #ifdef SWOOLE_SOCKETS_SUPPORT
php_swoole_convert_to_socket(int sock)380 php_socket *php_swoole_convert_to_socket(int sock) {
381     php_socket *socket_object;
382 #if PHP_VERSION_ID < 80000
383     socket_object = (php_socket *) emalloc(sizeof *socket_object);
384     sw_memset_zero(socket_object, sizeof(*socket_object));
385     socket_object->bsd_socket = sock;
386     socket_object->blocking = 1;
387 
388     struct sockaddr_storage addr;
389     socklen_t addr_len = sizeof(addr);
390 
391     if (getsockname(sock, (struct sockaddr *) &addr, &addr_len) == 0) {
392         socket_object->type = addr.ss_family;
393     } else {
394         php_swoole_sys_error(E_WARNING, "unable to obtain socket family");
395     _error:
396         efree(socket_object);
397         return nullptr;
398     }
399 
400     int t = fcntl(sock, F_GETFL);
401     if (t == -1) {
402         php_swoole_sys_error(E_WARNING, "unable to obtain blocking state");
403         goto _error;
404     } else {
405         socket_object->blocking = !(t & O_NONBLOCK);
406     }
407 #else
408     zval zsocket;
409     object_init_ex(&zsocket, socket_ce);
410     socket_object = Z_SOCKET_P(&zsocket);
411     socket_import_file_descriptor(sock, socket_object);
412 #endif
413     return socket_object;
414 }
415 #endif
416 
event_check_reactor()417 static void event_check_reactor() {
418     php_swoole_check_reactor();
419 
420     if (!swoole_event_isset_handler(SW_FD_USER)) {
421         swoole_event_set_handler(SW_FD_USER | SW_EVENT_READ, event_readable_callback);
422         swoole_event_set_handler(SW_FD_USER | SW_EVENT_WRITE, event_writable_callback);
423         swoole_event_set_handler(SW_FD_USER | SW_EVENT_ERROR, event_error_callback);
424     }
425 }
426 
event_get_socket(int socket_fd)427 static Socket *event_get_socket(int socket_fd) {
428     auto i = event_socket_map.find(socket_fd);
429     if (i == event_socket_map.end()) {
430         return nullptr;
431     }
432     return i->second;
433 }
434 
PHP_FUNCTION(swoole_event_add)435 static PHP_FUNCTION(swoole_event_add) {
436     zval *zfd;
437     zend_fcall_info fci_read = empty_fcall_info;
438     zend_fcall_info_cache fci_cache_read = empty_fcall_info_cache;
439     zend_fcall_info fci_write = empty_fcall_info;
440     zend_fcall_info_cache fci_cache_write = empty_fcall_info_cache;
441     zend_long events = SW_EVENT_READ;
442 
443     ZEND_PARSE_PARAMETERS_START(1, 4)
444     Z_PARAM_ZVAL(zfd)
445     Z_PARAM_OPTIONAL
446     Z_PARAM_FUNC_EX(fci_read, fci_cache_read, 1, 0)
447     Z_PARAM_FUNC_EX(fci_write, fci_cache_write, 1, 0)
448     Z_PARAM_LONG(events)
449     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
450 
451     if (fci_read.size == 0 && fci_write.size == 0) {
452         php_swoole_fatal_error(E_WARNING, "both read and write callbacks are empty");
453         RETURN_FALSE;
454     }
455 
456     int socket_fd = php_swoole_convert_to_fd(zfd);
457     if (socket_fd < 0) {
458         php_swoole_fatal_error(E_WARNING, "unknown fd type");
459         RETURN_FALSE;
460     }
461     if (socket_fd == 0 && (events & SW_EVENT_WRITE)) {
462         php_swoole_fatal_error(E_WARNING, "invalid socket fd [%d]", socket_fd);
463         RETURN_FALSE;
464     }
465     if (event_socket_map.find(socket_fd) != event_socket_map.end()) {
466         php_swoole_fatal_error(E_WARNING, "already exist");
467         RETURN_FALSE;
468     }
469     if (!(events & (SW_EVENT_WRITE | SW_EVENT_READ))) {
470         php_swoole_fatal_error(E_WARNING, "invalid events");
471         RETURN_FALSE;
472     }
473 
474     EventObject *peo = (EventObject *) ecalloc(1, sizeof(*peo));
475 
476     Z_TRY_ADDREF_P(zfd);
477     peo->zsocket = *zfd;
478 
479     if (fci_read.size != 0) {
480         sw_zend_fci_cache_persist(&fci_cache_read);
481         peo->fci_cache_read = fci_cache_read;
482     }
483     if (fci_write.size != 0) {
484         sw_zend_fci_cache_persist(&fci_cache_write);
485         peo->fci_cache_write = fci_cache_write;
486     }
487 
488     event_check_reactor();
489 
490     Socket *socket = swoole::make_socket(socket_fd, SW_FD_USER);
491     if (!socket) {
492         RETURN_FALSE;
493     }
494 
495     socket->set_nonblock();
496     socket->object = peo;
497 
498     if (swoole_event_add(socket, events) < 0) {
499         php_swoole_fatal_error(E_WARNING, "swoole_event_add failed");
500         socket->free();
501         event_object_free(peo);
502         RETURN_FALSE;
503     }
504 
505     event_socket_map[socket_fd] = socket;
506 
507     RETURN_LONG(socket_fd);
508 }
509 
PHP_FUNCTION(swoole_event_write)510 static PHP_FUNCTION(swoole_event_write) {
511     zval *zfd;
512     char *data;
513     size_t len;
514 
515     if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &zfd, &data, &len) == FAILURE) {
516         RETURN_FALSE;
517     }
518 
519     if (len == 0) {
520         php_swoole_fatal_error(E_WARNING, "data empty");
521         RETURN_FALSE;
522     }
523 
524     int socket_fd = php_swoole_convert_to_fd(zfd);
525     if (socket_fd < 0) {
526         php_swoole_fatal_error(E_WARNING, "unknown type");
527         RETURN_FALSE;
528     }
529 
530     Socket *socket = event_get_socket(socket_fd);
531     if (socket == nullptr) {
532         php_swoole_fatal_error(E_WARNING, "socket[%d] is not found in the reactor", socket_fd);
533         RETURN_FALSE;
534     }
535 
536     event_check_reactor();
537     if (swoole_event_write(socket, data, len) < 0) {
538         RETURN_FALSE;
539     } else {
540         RETURN_TRUE;
541     }
542 }
543 
PHP_FUNCTION(swoole_event_set)544 static PHP_FUNCTION(swoole_event_set) {
545     if (!sw_reactor()) {
546         php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call swoole_event_set");
547         RETURN_FALSE;
548     }
549 
550     zval *zfd;
551     zend_fcall_info fci_read = empty_fcall_info;
552     zend_fcall_info_cache fci_cache_read = empty_fcall_info_cache;
553     zend_fcall_info fci_write = empty_fcall_info;
554     zend_fcall_info_cache fci_cache_write = empty_fcall_info_cache;
555     zend_long events = 0;
556 
557     ZEND_PARSE_PARAMETERS_START(1, 4)
558     Z_PARAM_ZVAL(zfd)
559     Z_PARAM_OPTIONAL
560     Z_PARAM_FUNC_EX(fci_read, fci_cache_read, 1, 0)
561     Z_PARAM_FUNC_EX(fci_write, fci_cache_write, 1, 0)
562     Z_PARAM_LONG(events)
563     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
564 
565     int socket_fd = php_swoole_convert_to_fd(zfd);
566     if (socket_fd < 0) {
567         php_swoole_fatal_error(E_WARNING, "unknown type");
568         RETURN_FALSE;
569     }
570 
571     Socket *socket = event_get_socket(socket_fd);
572     if (socket == nullptr) {
573         php_swoole_fatal_error(E_WARNING, "socket[%d] is not found in the reactor", socket_fd);
574         RETURN_FALSE;
575     }
576 
577     EventObject *reactor_fd = (EventObject *) socket->object;
578     if (fci_read.size != 0) {
579         if (reactor_fd->fci_cache_read.function_handler) {
580             sw_zend_fci_cache_discard(&reactor_fd->fci_cache_read);
581         }
582         sw_zend_fci_cache_persist(&fci_cache_read);
583         reactor_fd->fci_cache_read = fci_cache_read;
584     }
585     if (fci_write.size != 0) {
586         if (reactor_fd->fci_cache_write.function_handler) {
587             sw_zend_fci_cache_discard(&reactor_fd->fci_cache_write);
588         }
589         sw_zend_fci_cache_persist(&fci_cache_write);
590         reactor_fd->fci_cache_write = fci_cache_write;
591     }
592 
593     if ((events & SW_EVENT_READ) && reactor_fd->fci_cache_read.function_handler == nullptr) {
594         php_swoole_fatal_error(
595             E_WARNING, "%s: unable to find read callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd);
596         RETURN_FALSE;
597     }
598     if ((events & SW_EVENT_WRITE) && reactor_fd->fci_cache_write.function_handler == nullptr) {
599         php_swoole_fatal_error(
600             E_WARNING, "%s: unable to find write callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd);
601         RETURN_FALSE;
602     }
603     if (swoole_event_set(socket, events) < 0) {
604         php_swoole_fatal_error(E_WARNING, "%s::set failed", ZSTR_VAL(swoole_event_ce->name));
605         RETURN_FALSE;
606     }
607 
608     RETURN_TRUE;
609 }
610 
PHP_FUNCTION(swoole_event_del)611 static PHP_FUNCTION(swoole_event_del) {
612     zval *zfd;
613 
614     if (!sw_reactor()) {
615         php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call swoole_event_del");
616         RETURN_FALSE;
617     }
618 
619     if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) {
620         RETURN_FALSE;
621     }
622 
623     int socket_fd = php_swoole_convert_to_fd(zfd);
624     if (socket_fd < 0) {
625         php_swoole_fatal_error(E_WARNING, "unknown type");
626         RETURN_FALSE;
627     }
628 
629     Socket *socket = event_get_socket(socket_fd);
630     if (!socket) {
631         RETURN_FALSE;
632     }
633     swoole_event_defer(event_object_free, socket->object);
634     int retval = swoole_event_del(socket);
635     event_socket_map.erase(socket_fd);
636     socket->fd = -1;
637     socket->free();
638     RETURN_BOOL(retval == SW_OK);
639 }
640 
PHP_FUNCTION(swoole_event_defer)641 static PHP_FUNCTION(swoole_event_defer) {
642     zend_fcall_info fci = empty_fcall_info;
643     zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache));
644 
645     ZEND_PARSE_PARAMETERS_START(1, 1)
646     Z_PARAM_FUNC(fci, *fci_cache)
647     ZEND_PARSE_PARAMETERS_END_EX(efree(fci_cache); RETURN_FALSE);
648 
649     php_swoole_check_reactor();
650 
651     sw_zend_fci_cache_persist(fci_cache);
652     swoole_event_defer(event_defer_callback, fci_cache);
653 
654     RETURN_TRUE;
655 }
656 
PHP_FUNCTION(swoole_event_cycle)657 static PHP_FUNCTION(swoole_event_cycle) {
658     if (!sw_reactor()) {
659         php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call %s", ZSTR_VAL(swoole_event_ce->name));
660         RETURN_FALSE;
661     }
662 
663     zend_fcall_info _fci = empty_fcall_info;
664     zend_fcall_info_cache _fci_cache = empty_fcall_info_cache;
665     zend_bool before = 0;
666 
667     ZEND_PARSE_PARAMETERS_START(1, 2)
668     Z_PARAM_FUNC_EX(_fci, _fci_cache, 1, 0)
669     Z_PARAM_OPTIONAL
670     Z_PARAM_BOOL(before)
671     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
672 
673     if (_fci.size == 0) {
674         if (sw_reactor()->idle_task.callback == nullptr) {
675             RETURN_FALSE;
676         } else {
677             swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->idle_task.data);
678             sw_reactor()->idle_task.callback = nullptr;
679             sw_reactor()->idle_task.data = nullptr;
680             RETURN_TRUE;
681         }
682     }
683 
684     zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache));
685 
686     *fci_cache = _fci_cache;
687     sw_zend_fci_cache_persist(fci_cache);
688 
689     if (!before) {
690         if (sw_reactor()->idle_task.data != nullptr) {
691             swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->idle_task.data);
692         }
693 
694         sw_reactor()->idle_task.callback = event_end_callback;
695         sw_reactor()->idle_task.data = fci_cache;
696     } else {
697         if (sw_reactor()->future_task.data != nullptr) {
698             swoole_event_defer(sw_zend_fci_cache_free, sw_reactor()->future_task.data);
699         }
700 
701         sw_reactor()->future_task.callback = event_end_callback;
702         sw_reactor()->future_task.data = fci_cache;
703         // Registration onBegin callback function
704         sw_reactor()->activate_future_task();
705     }
706 
707     RETURN_TRUE;
708 }
709 
PHP_FUNCTION(swoole_event_exit)710 static PHP_FUNCTION(swoole_event_exit) {
711     php_swoole_event_exit();
712 }
713 
PHP_FUNCTION(swoole_event_wait)714 static PHP_FUNCTION(swoole_event_wait) {
715     if (!sw_reactor()) {
716         return;
717     }
718     php_swoole_event_wait();
719 }
720 
PHP_FUNCTION(swoole_event_rshutdown)721 static PHP_FUNCTION(swoole_event_rshutdown) {
722     /* prevent the program from jumping out of the rshutdown */
723     zend_try {
724         if (!sw_reactor()) {
725             return;
726         }
727         // when throw Exception, do not show the info
728         if (!sw_reactor()->bailout) {
729             php_swoole_fatal_error(E_DEPRECATED, "Event::wait() in shutdown function is deprecated");
730         }
731         php_swoole_event_wait();
732     }
733     zend_end_try();
734 }
735 
PHP_FUNCTION(swoole_event_dispatch)736 static PHP_FUNCTION(swoole_event_dispatch) {
737     if (!sw_reactor()) {
738         RETURN_FALSE;
739     }
740     sw_reactor()->once = true;
741 
742 #ifdef HAVE_SIGNALFD
743     if (sw_reactor()->check_signalfd) {
744         swoole_signalfd_setup(sw_reactor());
745     }
746 #endif
747 
748     if (sw_reactor()->wait(nullptr) < 0) {
749         php_swoole_sys_error(E_ERROR, "reactor wait failed");
750     }
751 
752     sw_reactor()->once = false;
753     RETURN_TRUE;
754 }
755 
PHP_FUNCTION(swoole_event_isset)756 static PHP_FUNCTION(swoole_event_isset) {
757     if (!sw_reactor()) {
758         RETURN_FALSE;
759     }
760 
761     zval *zfd;
762     zend_long events = SW_EVENT_READ | SW_EVENT_WRITE;
763 
764     if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &zfd, &events) == FAILURE) {
765         RETURN_FALSE;
766     }
767 
768     int socket_fd = php_swoole_convert_to_fd(zfd);
769     if (socket_fd < 0) {
770         php_swoole_fatal_error(E_WARNING, "unknown type");
771         RETURN_FALSE;
772     }
773 
774     Socket *_socket = event_get_socket(socket_fd);
775     if (_socket == nullptr || _socket->removed) {
776         RETURN_FALSE;
777     }
778     if (_socket->events & events) {
779         RETURN_TRUE;
780     } else {
781         RETURN_FALSE;
782     }
783 }
784