int api_tcp_accept(api_tcp_listener_t* listener, api_tcp_t* tcp) { api_tcp_listener_accept_t accept; if (listener->loop->terminated) return API__TERMINATE; if (listener->status.closed || listener->status.terminated || listener->status.error != API__OK) return listener->status.error; accept.task = listener->loop->scheduler.current; accept.tcp = tcp; accept.success = 0; tcp->address.address.ss_family = listener->os_linux.af; tcp->address.length = sizeof(tcp->address.address); listener->os_linux.reserved = &accept; api_loop_read_add(listener->loop, listener->fd, &listener->os_linux.e); api_task_sleep(accept.task); api_loop_read_del(listener->loop, listener->fd, &listener->os_linux.e); listener->os_linux.reserved = 0; if (accept.success) return API__OK; return listener->status.error; }
int api_loop_stop_and_wait(api_loop_t* current, api_loop_t* loop) { int error = 0; error = api_wait_exec(current, loop, 0); if (error != API__OK) return error; error = api_loop_stop(loop); if (error != API__OK) return error; api_task_sleep(current->base.scheduler.current); return API__OK; }
int api_async_exec(api_loop_t* current, api_loop_t* loop, api_loop_fn callback, void* arg, size_t stack_size) { api_exec_t exec; exec.async.loop = loop; exec.async.handler = api_async_exec_handler; exec.async.callback = callback; exec.async.arg = arg; exec.async.stack_size = stack_size; exec.loop = current; exec.task = current->base.scheduler.current; exec.result = 0; if (!PostQueuedCompletionStatus(loop->iocp, sizeof(exec), (ULONG_PTR)&g_api_async_processor, (LPOVERLAPPED)&exec)) { exec.result = api_error_translate(GetLastError()); return exec.result; } api_task_sleep(current->base.scheduler.current); return exec.result; }
size_t api_stream_file_on_write(struct api_filter_t* filter, const char* buffer, size_t length) { api_stream_t* stream = filter->stream; api_stream_file_write_t write; api_timer_t timeout; uint64_t timeout_value = stream->write_timeout; struct timespec start, end, elapsed; uint64_t done = 0; int result; if (length == 0) return length; if (stream->status.write_timeout || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) return 0; if (stream->loop->base.terminated) return 0; if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = write.task; api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value); } clock_gettime(CLOCK_MONOTONIC, &start); do { bzero(&write, sizeof(struct api_stream_file_write_t)); write.aio.aio_fildes = stream->fd; write.aio.aio_buf = (char*)buffer + done; write.aio.aio_nbytes = length - done; write.aio.aio_offset = stream->impl.file.write_offset; write.aio.aio_sigevent.sigev_notify = SIGEV_THREAD; write.aio.aio_sigevent.sigev_notify_function = aio_write_completion_handler; write.aio.aio_sigevent.sigev_notify_attributes = 0; write.aio.aio_sigevent.sigev_value.sival_ptr = &write; write.done = 0; write.loop = stream->loop; write.task = stream->loop->base.scheduler.current; result = aio_write(&write.aio); if (result == -1) { stream->status.error = api_error_translate(errno); stream->filter_head->on_error(stream->filter_head, stream->status.error); break; } api_task_sleep(write.task); if (stream->loop->base.terminated) break; if (stream->status.write_timeout || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) break; if (timeout_value > 0 && timeout.elapsed) break; if (write.error != 0) { stream->status.error = write.error; break; } stream->impl.file.write_offset += write.done; done += write.done; } while (done < length); clock_gettime(CLOCK_MONOTONIC, &end); if (end.tv_nsec - start.tv_nsec < 0) { elapsed.tv_sec = end.tv_sec - start.tv_sec - 1; elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { elapsed.tv_sec = end.tv_sec - start.tv_sec; elapsed.tv_nsec = end.tv_nsec - start.tv_nsec; } if (timeout_value > 0) api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0); stream->write_bandwidth.sent += done; stream->write_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000; if (timeout_value > 0 && timeout.elapsed) { stream->status.write_timeout = 1; stream->filter_head->on_write_timeout(stream->filter_head); result = aio_cancel(stream->fd, &write.aio); /* handle error */ return 0; } else { return done; } }
size_t api_stream_file_on_read(struct api_filter_t* filter, char* buffer, size_t length) { api_stream_t* stream = filter->stream; api_stream_file_read_t read; api_timer_t timeout; struct timespec start, end, elapsed; uint64_t timeout_value = stream->read_timeout; int result; if (length == 0) return length; if (stream->status.read_timeout || stream->status.eof || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) return 0; bzero(&read, sizeof(struct api_stream_file_read_t)); read.aio.aio_fildes = stream->fd; read.aio.aio_buf = buffer; read.aio.aio_nbytes = length; read.aio.aio_offset = stream->impl.file.read_offset; read.aio.aio_sigevent.sigev_notify = SIGEV_THREAD; read.aio.aio_sigevent.sigev_notify_function = aio_read_completion_handler; read.aio.aio_sigevent.sigev_notify_attributes = 0; read.aio.aio_sigevent.sigev_value.sival_ptr = &read; read.done = 0; read.loop = stream->loop; read.task = stream->loop->base.scheduler.current; result = aio_read(&read.aio); if (result == -1) { stream->status.error = api_error_translate(errno); stream->filter_head->on_error(stream->filter_head, stream->status.error); return 0; } if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = read.task; api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value); } clock_gettime(CLOCK_MONOTONIC, &start); api_task_sleep(read.task); clock_gettime(CLOCK_MONOTONIC, &end); if (end.tv_nsec - start.tv_nsec < 0) { elapsed.tv_sec = end.tv_sec - start.tv_sec - 1; elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { elapsed.tv_sec = end.tv_sec - start.tv_sec; elapsed.tv_nsec = end.tv_nsec - start.tv_nsec; } if (timeout_value > 0) api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0); stream->read_bandwidth.read += read.done; stream->read_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000; if (timeout_value > 0 && timeout.elapsed) { stream->status.read_timeout = 1; stream->filter_head->on_read_timeout(stream->filter_head); result = aio_cancel(stream->fd, &read.aio); /* handle error */ return 0; } else { stream->impl.file.read_offset += read.done; if (read.done == 0) stream->status.eof = 1; if (read.error != 0) stream->status.error = read.error; return read.done; } }
size_t api_stream_on_write(struct api_filter_t* filter, const char* buffer, size_t length) { api_stream_t* stream = filter->stream; api_stream_write_t write; api_timer_t timeout; struct timespec start, end, elapsed; uint64_t timeout_value = stream->write_timeout; if (length == 0) return length; if (stream->status.write_timeout || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) return -1; if (stream->loop->base.terminated) return -1; write.buffer = buffer; write.length = length; write.offset = 0; write.task = stream->loop->base.scheduler.current; stream->os_linux.reserved[1] = &write; if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = write.task; api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value); } clock_gettime(CLOCK_MONOTONIC, &start); api_loop_write_add(stream->loop, stream->fd, &stream->os_linux.e); do { api_task_sleep(write.task); if (stream->status.write_timeout || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) break; if (timeout_value > 0 && timeout.elapsed) break; } while (write.offset < write.length); api_loop_write_del(stream->loop, stream->fd, &stream->os_linux.e); clock_gettime(CLOCK_MONOTONIC, &end); if (end.tv_nsec - start.tv_nsec < 0) { elapsed.tv_sec = end.tv_sec - start.tv_sec - 1; elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { elapsed.tv_sec = end.tv_sec - start.tv_sec; elapsed.tv_nsec = end.tv_nsec - start.tv_nsec; } if (timeout_value > 0) api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0); stream->os_linux.reserved[1] = 0; stream->write_bandwidth.sent += write.offset; stream->write_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000; if (timeout_value > 0 && timeout.elapsed) { stream->status.write_timeout = 1; stream->filter_head->on_write_timeout(stream->filter_head); } return write.offset; }
size_t api_stream_on_read(struct api_filter_t* filter, char* buffer, size_t length) { api_stream_t* stream = filter->stream; api_stream_read_t read; api_timer_t timeout; struct timespec start, end, elapsed; uint64_t timeout_value = stream->read_timeout; if (length == 0) return length; if (stream->status.read_timeout || stream->status.eof || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) return 0; read.buffer = buffer; read.length = length; read.done = 0; read.task = stream->loop->base.scheduler.current; stream->os_linux.reserved[0] = &read; if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = read.task; api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value); } clock_gettime(CLOCK_MONOTONIC, &start); api_loop_read_add(stream->loop, stream->fd, &stream->os_linux.e); api_task_sleep(read.task); api_loop_read_del(stream->loop, stream->fd, &stream->os_linux.e); clock_gettime(CLOCK_MONOTONIC, &end); if (end.tv_nsec - start.tv_nsec < 0) { elapsed.tv_sec = end.tv_sec - start.tv_sec - 1; elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { elapsed.tv_sec = end.tv_sec - start.tv_sec; elapsed.tv_nsec = end.tv_nsec - start.tv_nsec; } if (timeout_value > 0) api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0); stream->os_linux.reserved[0] = 0; stream->read_bandwidth.read += read.done; stream->read_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000; if (timeout_value > 0 && timeout.elapsed) { stream->status.read_timeout = 1; stream->filter_head->on_read_timeout(stream->filter_head); return 0; } else { return read.done; } }
int api_tcp_connect(api_tcp_t* tcp, api_loop_t* loop, const char* ip, int port, uint64_t tmeout) { struct sockaddr_in* addr_in = (struct sockaddr_in*)&tcp->address.address; struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&tcp->address.address; struct sockaddr* a = (struct sockaddr*)&tcp->address.address; api_timer_t timeout; uint64_t timeout_value = tmeout; HANDLE handle; DWORD dwSent = 0; DWORD sys_error = 0; BOOL result; BOOL completed = FALSE; int af; int error = API__OK; memset(tcp, 0, sizeof(*tcp)); if (loop->terminated) { tcp->stream.status.terminated = 1; return API__TERMINATE; } tcp->stream.os_win.processor = api_tcp_connect_processor; tcp->stream.os_win.reserved[0] = loop->scheduler.current; if (strchr(ip, ':') == 0) { // ipv4 tcp->stream.fd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); af = AF_INET; tcp->address.length = sizeof(struct sockaddr_in); addr_in->sin_family = AF_INET; addr_in->sin_addr.s_addr = INADDR_ANY; addr_in->sin_port = 0; } else { // ipv6 tcp->stream.fd = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); af = AF_INET6; tcp->address.length = sizeof(struct sockaddr_in6); addr_in6->sin6_family = AF_INET6; addr_in6->sin6_addr = in6addr_any; addr_in6->sin6_port = 0; } error = api_socket_non_block(tcp->stream.fd, 1); error = api_socket_recv_buffer_size(tcp->stream.fd, 0); error = api_socket_send_buffer_size(tcp->stream.fd, 0); error = api_tcp_nodelay(tcp->stream.fd, 1); if (0 != bind(tcp->stream.fd, a, tcp->address.length)) { error = api_error_translate(WSAGetLastError()); closesocket(tcp->stream.fd); return error; } handle = CreateIoCompletionPort((HANDLE)tcp->stream.fd, loop->iocp, (ULONG_PTR)&tcp->stream.os_win, 0); if (handle == NULL) { sys_error = GetLastError(); closesocket(tcp->stream.fd); return api_error_translate(sys_error); } SetFileCompletionNotificationModes((HANDLE)tcp->stream.fd, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); if (af == AF_INET) { addr_in->sin_family = AF_INET; addr_in->sin_addr.s_addr = inet_addr(ip); addr_in->sin_port = htons(port); } else { addr_in6->sin6_family = AF_INET6; addr_in6->sin6_addr = in6addr_any; addr_in6->sin6_port = htons(port); inet_pton(AF_INET6, ip, (void*)&addr_in6->sin6_addr); } if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = loop->scheduler.current; api_timeout_exec(&loop->timeouts, &timeout, timeout_value); } result = lpfnConnectEx(tcp->stream.fd, a, tcp->address.length, NULL, 0, &dwSent, (LPOVERLAPPED)&tcp->stream.os_win.read); if (!result) { sys_error = WSAGetLastError(); if (sys_error == ERROR_SUCCESS) { completed = TRUE; } else if (sys_error != WSA_IO_PENDING) { completed = TRUE; error = api_error_translate(sys_error); tcp->stream.status.error = error; } } else { completed = TRUE; } if (!completed) api_task_sleep(loop->scheduler.current); if (timeout_value > 0) api_timeout_exec(&loop->timeouts, &timeout, 0); if (timeout_value > 0 && timeout.elapsed) { tcp->stream.status.read_timeout = 1; if (API__OK != error) error = api_error_translate(ERROR_TIMEOUT); } if (API__OK == error) { api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd); tcp->stream.loop = loop; api_loop_ref(loop); return API__OK; } else { closesocket(tcp->stream.fd); } return error; }
int api_tcp_accept(api_tcp_listener_t* listener, api_tcp_t* tcp) { api_tcp_listener_accept_t accept; CHAR buffer[2 * (sizeof(SOCKADDR_IN) + 16)]; SOCKADDR *lpLocalSockaddr = NULL; SOCKADDR *lpRemoteSockaddr = NULL; int localSockaddrLen = 0; int remoteSockaddrLen = 0; DWORD dwBytes; BOOL result; BOOL success = FALSE; BOOL completed = FALSE; DWORD sys_error; int error = API__OK; memset(&listener->os_win.ovl, 0, sizeof(listener->os_win.ovl)); if (listener->loop->terminated) return API__TERMINATE; if (listener->status.closed || listener->status.terminated || listener->status.error != API__OK) return listener->status.error; accept.task = listener->loop->scheduler.current; listener->os_win.reserved = &accept; do { success = FALSE; completed = FALSE; tcp->stream.fd = WSASocket(listener->os_win.af, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); result = lpfnAcceptEx(listener->fd, tcp->stream.fd, buffer, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, (LPOVERLAPPED)&listener->os_win.ovl); if (!result) { sys_error = WSAGetLastError(); if (sys_error == ERROR_SUCCESS) { completed = TRUE; } else if (sys_error != WSA_IO_PENDING) { listener->status.error = api_error_translate(sys_error); closesocket(tcp->stream.fd); break; } } else { completed = TRUE; } if (!completed) api_task_sleep(accept.task); lpfnGetAcceptExSockaddrs(buffer, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &lpLocalSockaddr, &localSockaddrLen, &lpRemoteSockaddr, &remoteSockaddrLen); tcp->address.length = remoteSockaddrLen; memcpy(&tcp->address.address, lpRemoteSockaddr, remoteSockaddrLen); success = listener->on_accept(listener, tcp); if (success) { error = api_socket_non_block(tcp->stream.fd, 1); error = api_socket_recv_buffer_size(tcp->stream.fd, 0); error = api_socket_send_buffer_size(tcp->stream.fd, 0); error = api_tcp_nodelay(tcp->stream.fd, 1); api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd); } else { closesocket(tcp->stream.fd); } } while (!success); listener->os_win.reserved = 0; if (success) return API__OK; return listener->status.error; }
int api_tcp_connect(api_tcp_t* tcp, api_loop_t* loop, const char* ip, int port, uint64_t tmeout) { struct sockaddr_in* addr_in = (struct sockaddr_in*)&tcp->address.address; struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&tcp->address.address; struct sockaddr* a = (struct sockaddr*)&tcp->address.address; int error = API__OK; api_timer_t timeout; uint64_t timeout_value = tmeout; memset(tcp, 0, sizeof(*tcp)); if (loop->terminated) { tcp->stream.status.terminated = 1; return API__TERMINATE; } tcp->stream.os_linux.processor = api_tcp_connect_processor; tcp->stream.os_linux.reserved[0] = loop->scheduler.current; tcp->stream.os_linux.e.data.ptr = &tcp->stream.os_linux; if (strchr(ip, ':') == 0) { // ipv4 tcp->stream.fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); tcp->address.length = sizeof(struct sockaddr_in); addr_in->sin_family = AF_INET; addr_in->sin_addr.s_addr = inet_addr(ip); addr_in->sin_port = htons(port); } else { // ipv6 tcp->stream.fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); tcp->address.length = sizeof(struct sockaddr_in6); addr_in6->sin6_family = AF_INET6; addr_in6->sin6_addr = in6addr_any; addr_in6->sin6_port = htons(port); inet_pton(AF_INET6, ip, (void*)&addr_in6->sin6_addr.__in6_u); } error = api_socket_non_block(tcp->stream.fd, 1); error = api_socket_recv_buffer_size(tcp->stream.fd, 0); error = api_socket_send_buffer_size(tcp->stream.fd, 0); error = api_tcp_nodelay(tcp->stream.fd, 1); if (0 != connect(tcp->stream.fd, a, tcp->address.length)) { if (errno != EINPROGRESS) { error = api_error_translate(errno); } else { // wait needed tcp->stream.os_linux.e.events = EPOLLERR | EPOLLHUP | EPOLLRDHUP | EPOLLOUT; error = epoll_ctl(loop->epoll, EPOLL_CTL_ADD, tcp->stream.fd, &tcp->stream.os_linux.e); error = api_error_translate(error); if (API__OK == error) { error = api_loop_write_add(loop, tcp->stream.fd, &tcp->stream.os_linux.e); if (API__OK != error) { epoll_ctl(loop->epoll, EPOLL_CTL_DEL, tcp->stream.fd, &tcp->stream.os_linux.e); } else { if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = loop->scheduler.current; api_timeout_exec(&loop->timeouts, &timeout, timeout_value); } api_task_sleep(loop->scheduler.current); if (timeout_value > 0) api_timeout_exec(&loop->timeouts, &timeout, 0); if (timeout_value > 0 && timeout.elapsed) { tcp->stream.status.read_timeout = 1; error = api_error_translate(ETIMEDOUT); } if (tcp->stream.status.closed || tcp->stream.status.terminated || tcp->stream.status.error != API__OK) { epoll_ctl(loop->epoll, EPOLL_CTL_DEL, tcp->stream.fd, &tcp->stream.os_linux.e); } else { api_loop_write_del(loop, tcp->stream.fd, &tcp->stream.os_linux.e); } } } } } else { // completed immediately } if (API__OK == error && tcp->stream.status.closed == 0 && tcp->stream.status.terminated == 0 && tcp->stream.status.error == API__OK) { api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd); tcp->stream.loop = loop; api_loop_ref(loop); return API__OK; } close(tcp->stream.fd); if (API__OK != error) return error; if (API__OK != tcp->stream.status.error) return tcp->stream.status.error; return -1; }