static PHP_METHOD(swoole_coroutine_util, gethostbyname) { coro_check(TSRMLS_C); char *domain_name; zend_size_t l_domain_name; long family = AF_INET; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &domain_name, &l_domain_name, &family) == FAILURE) { RETURN_FALSE; } if (l_domain_name <= 0) { swoole_php_fatal_error(E_WARNING, "domain name is empty."); RETURN_FALSE; } if (family != AF_INET && family != AF_INET6) { swoole_php_fatal_error(E_WARNING, "unknown protocol family, must be AF_INET or AF_INET6."); RETURN_FALSE; } swAio_event ev; bzero(&ev, sizeof(swAio_event)); if (l_domain_name < SW_IP_MAX_LENGTH) { ev.nbytes = SW_IP_MAX_LENGTH; } else { ev.nbytes = l_domain_name + 1; } ev.buf = emalloc(ev.nbytes); if (!ev.buf) { swWarn("malloc failed."); RETURN_FALSE; } php_context *sw_current_context = emalloc(sizeof(php_context)); memcpy(ev.buf, domain_name, l_domain_name); ((char *) ev.buf)[l_domain_name] = 0; ev.flags = family; ev.type = SW_AIO_GETHOSTBYNAME; ev.object = sw_current_context; ev.callback = coro_dns_onResolveCompleted; php_swoole_check_aio(); if (swAio_dispatch(&ev) < 0) { efree(ev.buf); RETURN_FALSE; } coro_save(sw_current_context); coro_yield(); }
static int process_request_coro(coro_t *coro) { strbuf_t *strbuf = coro_malloc_full(coro, sizeof(*strbuf), strbuf_free); lwan_connection_t *conn = coro_get_data(coro); lwan_t *lwan = conn->thread->lwan; int fd = lwan_connection_get_fd(lwan, conn); char request_buffer[DEFAULT_BUFFER_SIZE]; lwan_value_t buffer = { .value = request_buffer, .len = 0 }; char *next_request = NULL; strbuf_init(strbuf); while (true) { lwan_request_t request = { .conn = conn, .fd = fd, .response = { .buffer = strbuf }, }; assert(conn->flags & CONN_IS_ALIVE); next_request = lwan_process_request(lwan, &request, &buffer, next_request); if (!next_request) break; coro_yield(coro, CONN_CORO_MAY_RESUME); if (UNLIKELY(!strbuf_reset_length(strbuf))) return CONN_CORO_ABORT; } return CONN_CORO_FINISHED; } static ALWAYS_INLINE void resume_coro_if_needed(struct death_queue_t *dq, lwan_connection_t *conn, int epoll_fd) { assert(conn->coro); if (!(conn->flags & CONN_SHOULD_RESUME_CORO)) return; lwan_connection_coro_yield_t yield_result = coro_resume(conn->coro); /* CONN_CORO_ABORT is -1, but comparing with 0 is cheaper */ if (yield_result < CONN_CORO_MAY_RESUME) { destroy_coro(dq, conn); return; } bool write_events; if (conn->flags & CONN_MUST_READ) { write_events = true; } else { bool should_resume_coro = (yield_result == CONN_CORO_MAY_RESUME); if (should_resume_coro) conn->flags |= CONN_SHOULD_RESUME_CORO; else conn->flags &= ~CONN_SHOULD_RESUME_CORO; write_events = (conn->flags & CONN_WRITE_EVENTS); if (should_resume_coro == write_events) return; } struct epoll_event event = { .events = events_by_write_flag[write_events], .data.ptr = conn }; int fd = lwan_connection_get_fd(dq->lwan, conn); if (UNLIKELY(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)) lwan_status_perror("epoll_ctl"); conn->flags ^= CONN_WRITE_EVENTS; } static void death_queue_kill_waiting(struct death_queue_t *dq) { dq->time++; while (!death_queue_empty(dq)) { lwan_connection_t *conn = death_queue_idx_to_node(dq, dq->head.next); if (conn->time_to_die > dq->time) return; destroy_coro(dq, conn); } /* Death queue exhausted: reset epoch */ dq->time = 0; }
static PHP_METHOD(swoole_coroutine_util, writeFile) { coro_check(TSRMLS_C); char *filename = NULL; size_t l_filename = 0; char *data = NULL; size_t l_data = 0; zend_long flags = 0; #ifdef FAST_ZPP ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STRING(filename, l_filename) Z_PARAM_STRING(data, l_data) Z_PARAM_OPTIONAL Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &filename, &l_filename, &data, &l_data, &flags) == FAILURE) { return; } #endif swAio_event ev; bzero(&ev, sizeof(swAio_event)); ev.nbytes = l_data; ev.buf = data; php_context *context = emalloc(sizeof(php_context)); ev.type = SW_AIO_WRITE_FILE; ev.object = context; ev.callback = aio_onWriteFileCompleted; ev.req = estrndup(filename, l_filename); ev.flags = O_CREAT | O_WRONLY; if (flags & PHP_FILE_APPEND) { ev.flags |= O_APPEND; } else { ev.flags |= O_TRUNC; } if (!SwooleAIO.init) { php_swoole_check_reactor(); swAio_init(); } swTrace("writeFile(%s, %ld)", filename, ev.nbytes); int ret = swAio_dispatch(&ev); if (ret < 0) { efree(context); RETURN_FALSE; } context->onTimeout = NULL; context->state = SW_CORO_CONTEXT_RUNNING; coro_save(context); coro_yield(); }
static PHP_METHOD(swoole_coroutine_util, fwrite) { coro_check(TSRMLS_C); zval *handle; char *str; zend_size_t l_str; zend_long length = 0; #ifdef FAST_ZPP ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_RESOURCE(handle) Z_PARAM_STRING(str, l_str) Z_PARAM_OPTIONAL Z_PARAM_LONG(length) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &handle, &str, &l_str, &length) == FAILURE) { return; } #endif int fd = swoole_convert_to_fd(handle TSRMLS_CC); off_t _seek = lseek(fd, 0, SEEK_CUR); if (length <= 0 || length > l_str) { length = l_str; } swAio_event ev; bzero(&ev, sizeof(swAio_event)); ev.nbytes = length; ev.buf = estrndup(str, length); if (!ev.buf) { RETURN_FALSE; } php_context *context = emalloc(sizeof(php_context)); ev.flags = 0; ev.type = SW_AIO_WRITE; ev.object = context; ev.callback = aio_onWriteCompleted; ev.fd = fd; ev.offset = _seek; php_swoole_check_aio(); swTrace("fd=%d, offset=%ld, length=%ld", fd, ev.offset, ev.nbytes); int ret = swAio_dispatch(&ev); if (ret < 0) { efree(context); RETURN_FALSE; } context->onTimeout = NULL; context->state = SW_CORO_CONTEXT_RUNNING; coro_save(context); coro_yield(); }
static PHP_METHOD(swoole_coroutine_util, fgets) { coro_check(TSRMLS_C); zval *handle; php_stream *stream; #ifdef FAST_ZPP ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_RESOURCE(handle) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &handle) == FAILURE) { return; } #endif int fd = swoole_convert_to_fd(handle TSRMLS_CC); swAio_event ev; bzero(&ev, sizeof(swAio_event)); php_stream_from_res(stream, Z_RES_P(handle)); if (stream->readbuf == NULL) { stream->readbuflen = stream->chunk_size; stream->readbuf = emalloc(stream->chunk_size); } ev.nbytes = stream->readbuflen; ev.buf = stream->readbuf; if (!ev.buf) { RETURN_FALSE; } php_context *context = emalloc(sizeof(php_context)); ev.flags = 0; ev.type = SW_AIO_STREAM_GET_LINE; ev.object = context; ev.callback = aio_onStreamGetLineCompleted; ev.fd = fd; ev.offset = stream->readpos; ev.req = (void *) (long) stream->writepos; if (!SwooleAIO.init) { php_swoole_check_reactor(); swAio_init(); } swTrace("fd=%d, offset=%ld, length=%ld", fd, ev.offset, ev.nbytes); int ret = swAio_dispatch(&ev); if (ret < 0) { efree(context); RETURN_FALSE; } context->coro_params = *handle; context->onTimeout = NULL; context->state = SW_CORO_CONTEXT_RUNNING; coro_save(context); coro_yield(); }
static PHP_METHOD(swoole_coroutine_util, fread) { coro_check(TSRMLS_C); zval *handle; zend_long length = 0; #ifdef FAST_ZPP ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_RESOURCE(handle) Z_PARAM_OPTIONAL Z_PARAM_LONG(length) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &handle, &length) == FAILURE) { return; } #endif int fd = swoole_convert_to_fd(handle TSRMLS_CC); struct stat file_stat; if (fstat(fd, &file_stat) < 0) { RETURN_FALSE; } off_t _seek = lseek(fd, 0, SEEK_CUR); if (length <= 0 || file_stat.st_size - _seek < length) { length = file_stat.st_size - _seek; } swAio_event ev; bzero(&ev, sizeof(swAio_event)); ev.nbytes = length + 1; ev.buf = emalloc(ev.nbytes); if (!ev.buf) { RETURN_FALSE; } php_context *context = emalloc(sizeof(php_context)); ((char *) ev.buf)[length] = 0; ev.flags = 0; ev.type = SW_AIO_READ; ev.object = context; ev.callback = aio_onReadCompleted; ev.fd = fd; ev.offset = _seek; if (!SwooleAIO.init) { php_swoole_check_reactor(); swAio_init(); } swTrace("fd=%d, offset=%ld, length=%ld", fd, ev.offset, ev.nbytes); int ret = swAio_dispatch(&ev); if (ret < 0) { efree(context); RETURN_FALSE; } context->onTimeout = NULL; context->state = SW_CORO_CONTEXT_RUNNING; coro_save(context); coro_yield(); }
static PHP_METHOD(swoole_coroutine_util, getaddrinfo) { coro_check(TSRMLS_C); char *hostname; zend_size_t l_hostname; long family = AF_INET; long socktype = SOCK_STREAM; long protocol = IPPROTO_TCP; char *service = NULL; zend_size_t l_service = 0; if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "s|llls", &hostname, &l_hostname, &family, socktype, &protocol, &hostname, &l_hostname) == FAILURE) { RETURN_FALSE; } if (l_hostname <= 0) { swoole_php_fatal_error(E_WARNING, "hostname is empty."); RETURN_FALSE; } if (family != AF_INET && family != AF_INET6) { swoole_php_fatal_error(E_WARNING, "unknown protocol family, must be AF_INET or AF_INET6."); RETURN_FALSE; } swAio_event ev; bzero(&ev, sizeof(swAio_event)); swRequest_getaddrinfo *req = emalloc(sizeof(swRequest_getaddrinfo)); bzero(req, sizeof(swRequest_getaddrinfo)); php_context *sw_current_context = emalloc(sizeof(php_context)); ev.type = SW_AIO_GETADDRINFO; ev.object = sw_current_context; ev.callback = coro_dns_onGetaddrinfoCompleted; ev.req = req; req->hostname = estrndup(hostname, l_hostname); req->family = family; req->socktype = socktype; req->protocol = protocol; if (service) { req->service = estrndup(service, l_service); } if (family == AF_INET) { req->result = ecalloc(SW_DNS_HOST_BUFFER_SIZE, sizeof(struct sockaddr_in)); } else { req->result = ecalloc(SW_DNS_HOST_BUFFER_SIZE, sizeof(struct sockaddr_in6)); } php_swoole_check_aio(); if (swAio_dispatch(&ev) < 0) { efree(ev.buf); RETURN_FALSE; } coro_save(sw_current_context); coro_yield(); }