myxce/src/lib/netio.c
2025-11-05 16:58:57 +08:00

687 lines
12 KiB
C

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include "dataq.h"
#include "netio.h"
#include "request.h"
#include "utils.h"
static int iomgr_update_poll(iomgr_t*, endpoint_t*, int);
static void endpoint_fini(endpoint_t*);
static int socket_create(int domain,
int type,
int port,
int backlog)
{
int sock;
int opt = 1;
if ((domain < 0) || (type < 0)) {
return -EINVAL;
}
sock = socket(domain, type, 0);
if (sock < 0) {
SET_ERROR(errno);
return -errno;
}
fcntl(sock, F_SETFD, (fcntl(sock, F_GETFD) | FD_CLOEXEC));
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
close(sock);
return -1;
}
if (port > 0) {
struct sockaddr_in sin;
memset((char*)&sin, 0, sizeof(sin));
sin.sin_family = domain;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
int rc;
SET_ERROR(errno);
rc = -errno;
(void)close(sock);
return rc;
}
if (backlog > 0) {
if (listen(sock, backlog) < 0) {
int rc;
SET_ERROR(errno);
rc = -errno;
(void)close(sock);
return rc;
}
}
}
return sock;
}
static int socket_connect(int sock,
char* host,
int port)
{
struct sockaddr_in server;
struct hostent* hp;
int rc;
server.sin_family = AF_INET;
hp = gethostbyname(host);
if (hp == NULL) {
SET_ERROR(errno);
rc = -errno;
return rc;
}
memcpy((char*)&server.sin_addr, (char*)hp->h_addr, (int)hp->h_length);
server.sin_port = htons(port);
rc = connect(sock, (struct sockaddr*)&server, sizeof(server));
if (rc < 0) {
SET_ERROR(errno);
return -errno;
}
return rc;
}
static int socket_accept(int sock)
{
socklen_t len;
int s;
struct sockaddr_in from;
len = sizeof(from);
s = accept(sock, (struct sockaddr*)&from, &len);
if (s < 0) {
SET_ERROR(errno);
return -errno;
}
fcntl(s, F_SETFD, (fcntl(s, F_GETFD) | FD_CLOEXEC));
return s;
}
int netio_write(int fd,
char* data,
int sz)
{
int nc = 0;
while (sz > 0) {
int rc;
rc = write(fd, data + nc, sz);
if (rc < 0) {
SET_ERROR(errno);
return -errno;
}
sz -= rc;
nc += rc;
}
return nc;
}
int netio_read(int fd,
char* data,
int sz)
{
int nc = 0;
while (sz > 0) {
int rc;
rc = read(fd, data + nc, sz);
if (rc < 0) {
SET_ERROR(errno);
return -errno;
}
if (rc == 0) {
/* network read return 0, it seems remote
* size has tear down the connection
*/
return -EPIPE;
}
sz -= rc;
nc += rc;
}
return nc;
}
int write_packet(int fd,
io_packet_t* pkt)
{
int rc;
rc = netio_write(fd, (char*)&(pkt->sz),
sizeof(pkt->sz));
if (rc < 0) {
return -errno;
}
rc = netio_write(fd, pkt->data, pkt->sz);
if (rc < 0) {
return -errno;
}
return rc;
}
int alloc_packet(io_packet_t* pkt)
{
if (pkt->sz <= 0) {
SET_ERROR(EINVAL);
return -EINVAL;
}
pkt->data = (char*)malloc(pkt->sz);
if (pkt->data == NULL) {
SET_ERROR(ENOMEM);
return -ENOMEM;
}
return 1;
}
int read_packet(int fd,
io_packet_t* pkt)
{
int rc;
rc = netio_read(fd, (char*)&(pkt->sz),
sizeof(pkt->sz));
if (rc < 0) {
return rc;
}
rc = alloc_packet(pkt);
if (rc < 0) {
return rc;
}
rc = netio_read(fd, pkt->data, pkt->sz);
if (rc < 0) {
free_packet(pkt);
}
return rc;
}
void free_packet(io_packet_t* pkt)
{
if (pkt->data != NULL) {
free(pkt->data);
pkt->data = NULL;
}
}
#define MAX_EPOLL_EVENTS (5)
static void* io_worker(void* args)
{
iomgr_t* iomgr = (iomgr_t*)args;
while (1) {
int nfds = -1;
int i;
struct epoll_event events[MAX_EPOLL_EVENTS];
nfds = epoll_wait(iomgr->epoll_fd, events,
MAX_EPOLL_EVENTS, -1);
if (nfds == -1) {
if (errno == EINTR) {
continue;
}
perror("epoll");
return NULL;
}
for (i = 0; i < nfds; i++) {
endpoint_t* p = (endpoint_t*)events[i].data.ptr;
if (p->handler != NULL) {
(p->handler)(&events[i]);
}
}
pthread_testcancel();
}
return NULL;
}
static int endpoint_calc_flags(endpoint_t* ep)
{
int flags = EPOLLIN;
if (ep->outq.head != NULL) {
flags = flags | EPOLLOUT;
}
return flags;
}
endpoint_t* endpoint_get()
{
endpoint_t* ep;
ep = (endpoint_t*)calloc(1, sizeof(*ep));
if (ep == NULL) {
SET_ERROR(errno);
return ep;
}
ep->fd = -1;
ep->idx = -1;
ep->iomgr = NULL;
ep->busy = 0;
ep->needfree = 1;
mylock_init(&(ep->lock));
return endpoint_ref(ep);
}
static void outq_item_killer(data_item_t* it)
{
if (it != NULL) {
ioctx_t* ctx = ITPTR_TO_IOCTX(it);
ioctx_deref(ctx, 1);
}
}
int endpoint_init(endpoint_t* ep,
int domain,
int type,
int port,
int backlog)
{
int fd = -1;
if (ep == NULL) {
SET_ERROR(EINVAL);
return -EINVAL;
}
fd = socket_create(domain, type,
port, backlog);
if (fd < 0) {
return -errno;
}
ep->flags = 0;
ep->fd = fd;
ep->owner = NULL;
ep->data = NULL;
ep->iomgr = NULL;
ep->busy = 0;
ep->needfree = 0;
dataq_init(&(ep->outq),
outq_item_killer);
mylock_init(&(ep->lock));
endpoint_ref(ep);
return fd;
}
int endpoint_connect(endpoint_t* ep,
char* host,
int port)
{
int rc = -1;
rc = socket_connect(ep->fd, host, port);
if (rc < 0) {
return rc;
}
ep->flags = 0;
return rc;
}
int endpoint_accept(endpoint_t* ep,
endpoint_t* ep2)
{
int sock = -1;
sock = socket_accept(ep->fd);
if (sock < 0) {
return sock;
}
ep2->fd = sock;
ep2->flags = 0;
return sock;
}
int endpoint_set_handler(endpoint_t* ep,
msghandler_t h,
void* owner)
{
if (ep->fd < 0) {
SET_ERROR(EINVAL);
return -EINVAL;
}
mylock_lock(&(ep->lock));
ep->handler = h;
ep->owner = owner;
mylock_unlock(&(ep->lock));
return 1;
}
int endpoint_enq(endpoint_t* ep,
data_item_t* it)
{
int rc = 0;
int flags;
if ((ep == NULL) || (it == NULL)) {
SET_ERROR(EINVAL);
return -EINVAL;
}
mylock_lock(&(ep->lock));
rc = dataq_enq(&(ep->outq), it);
flags = endpoint_calc_flags(ep);
if (flags != ep->flags) {
if (ep->iomgr != NULL) {
int rc1;
mylock_lock(&(ep->iomgr->lock));
rc1 = iomgr_update_poll(ep->iomgr,
ep, flags);
mylock_unlock(&(ep->iomgr->lock));
if (rc1 >= 0) {
ep->flags = flags;
} else {
WARN("rc1=%d < 0", rc1);
}
}
}
mylock_unlock(&(ep->lock));
return rc;
}
data_item_t* endpoint_deq(endpoint_t* ep)
{
data_item_t* it = NULL;
int flags;
if (ep == NULL) {
SET_ERROR(EINVAL);
return NULL;
}
mylock_lock(&(ep->lock));
it = dataq_deq(&(ep->outq), NULL);
flags = endpoint_calc_flags(ep);
if (flags != ep->flags) {
if (ep->iomgr != NULL) {
int rc;
mylock_lock(&(ep->iomgr->lock));
rc = iomgr_update_poll(ep->iomgr,
ep, flags);
mylock_unlock(&(ep->iomgr->lock));
if (rc >= 0) {
ep->flags = flags;
}
}
}
mylock_unlock(&(ep->lock));
return it;
}
endpoint_t* endpoint_ref(endpoint_t* ep)
{
mylock_lock(&(ep->lock));
ep->refcount++;
mylock_unlock(&(ep->lock));
return ep;
}
void endpoint_deref(endpoint_t* ep)
{
uint32_t refcount;
mylock_lock(&(ep->lock));
ep->refcount--;
refcount = ep->refcount;
mylock_unlock(&(ep->lock));
if (refcount == 0) {
endpoint_fini(ep);
}
}
uint64_t endpoint_getseq(endpoint_t* ep)
{
uint64_t seq;
mylock_lock(&(ep->lock));
seq = ep->seq++;
mylock_unlock(&(ep->lock));
return seq;
}
void endpoint_block_tasks(endpoint_t* ep)
{
TRACE("%s[%ld] block tasks for endpoint %p",
__func__, pthread_self(), ep);
mylock_lock(&(ep->lock));
ep->busy = 1;
mylock_unlock(&(ep->lock));
}
void endpoint_unblock_tasks(endpoint_t* ep)
{
TRACE("%s[%ld] unblock tasks for endpoint %p",
__func__, pthread_self(), ep);
mylock_lock(&(ep->lock));
ep->busy = 0;
mylock_unlock(&(ep->lock));
}
int endpoint_is_task_blocked(endpoint_t* ep)
{
int busy;
mylock_lock(&(ep->lock));
busy = ep->busy;
mylock_unlock(&(ep->lock));
TRACE("%s[%ld] endpoint %p task %s",
__func__, pthread_self(),
ep, (busy != 0) ? "blocked" : "not blocked");
return (busy != 0);
}
static void endpoint_fini(endpoint_t* ep)
{
int needfree = ep->needfree;
dataq_fini(&(ep->outq));
FREEUP(ep->data);
if (ep->fd >= 0) {
close(ep->fd);
}
ep->flags = 0;
ep->handler = NULL;
ep->owner = NULL;
if (needfree) {
FREEUP(ep);
}
}
int iomgr_init(iomgr_t* iomgr)
{
mylock_init(&(iomgr->lock));
iomgr->epoll_fd = epoll_create1(0);
if (iomgr->epoll_fd < 0) {
SET_ERROR(errno);
return -errno;
}
return 0;
}
static int iomgr_update_poll(iomgr_t* iomgr,
endpoint_t* ep,
int flags)
{
struct epoll_event ev;
ev.events = flags;
ev.data.ptr = ep;
return epoll_ctl(iomgr->epoll_fd,
EPOLL_CTL_MOD,
ep->fd, &ev);
}
int iomgr_monitor(iomgr_t* iomgr,
endpoint_t* ep)
{
struct epoll_event ev;
int rc;
int flags;
flags = EPOLLIN;
ev.events = flags;
ev.data.ptr = ep;
mylock_lock(&(ep->lock));
ep->iomgr = iomgr;
mylock_lock(&(iomgr->lock));
rc = epoll_ctl(iomgr->epoll_fd, EPOLL_CTL_ADD,
ep->fd, &ev);
mylock_unlock(&(iomgr->lock));
if (rc >= 0) {
ep->flags = flags;
}
mylock_unlock(&(ep->lock));
return rc;
}
int iomgr_unmonitor(iomgr_t* iomgr,
endpoint_t* ep)
{
int rc;
mylock_lock(&(iomgr->lock));
rc = epoll_ctl(iomgr->epoll_fd, EPOLL_CTL_DEL,
ep->fd, NULL);
if (rc < 0) {
SET_ERROR(errno);
rc = -errno;
}
mylock_unlock(&(iomgr->lock));
return rc;
}
int iomgr_start(iomgr_t* iomgr)
{
int rc;
rc = pthread_create(&(iomgr->worker), NULL, &io_worker, iomgr);
return rc;
}
int iomgr_fini(iomgr_t* iomgr)
{
return 0;
}
int iomgr_stop(iomgr_t* iomgr)
{
int rc;
void* res;
if (iomgr->worker == 0) {
return 0;
}
rc = pthread_cancel(iomgr->worker);
if (rc != 0) {
SET_ERROR(errno);
return -errno;
}
rc = pthread_join(iomgr->worker, &res);
if (rc != 0) {
SET_ERROR(errno);
return -errno;
}
if (res == PTHREAD_CANCELED) {
iomgr->worker = 0;
return 0;
}
return -1;
}