2019-09-06 23:53:10 +08:00

677 lines
17 KiB
C
Executable File

/*
+----------------------------------------------------------------------+
| Swoole |
+----------------------------------------------------------------------+
| Copyright (c) 2012-2018 The Swoole Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.0 of the Apache license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.apache.org/licenses/LICENSE-2.0.html |
| If you did not receive a copy of the Apache2.0 license and are unable|
| to obtain it through the world-wide-web, please send a note to |
| license@swoole.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
+----------------------------------------------------------------------+
*/
#include "swoole.h"
#include "async.h"
#include <sys/file.h>
#include <sys/stat.h>
swAsyncIO SwooleAIO;
swPipe swoole_aio_pipe;
static void swAioBase_destroy();
static int swAioBase_read(int fd, void *inbuf, size_t size, off_t offset);
static int swAioBase_write(int fd, void *inbuf, size_t size, off_t offset);
static int swAioBase_thread_onTask(swThreadPool *pool, void *task, int task_len);
static int swAioBase_onFinish(swReactor *reactor, swEvent *event);
static void swAio_handler_read(swAio_event *event);
static void swAio_handler_write(swAio_event *event);
static void swAio_handler_gethostbyname(swAio_event *event);
static void swAio_handler_getaddrinfo(swAio_event *event);
static void swAio_handler_stream_get_line(swAio_event *event);
static void swAio_handler_read_file(swAio_event *event);
static void swAio_handler_write_file(swAio_event *event);
static swThreadPool swAioBase_thread_pool;
static int swAioBase_pipe_read;
static int swAioBase_pipe_write;
int swAio_init(void)
{
if (SwooleAIO.init)
{
swWarn("AIO has already been initialized");
return SW_ERR;
}
if (!SwooleG.main_reactor)
{
swWarn("No eventloop, cannot initialized");
return SW_ERR;
}
return swAioBase_init(SW_AIO_EVENT_NUM);
}
void swAio_free(void)
{
if (!SwooleAIO.init)
{
return;
}
SwooleAIO.destroy();
SwooleAIO.init = 0;
}
/**
* for test
*/
void swAio_callback_test(swAio_event *aio_event)
{
printf("content=%s\n", (char *)aio_event->buf);
printf("fd: %d, request_type: %s, offset: %ld, length: %lu\n", aio_event->fd,
(aio_event == SW_AIO_READ) ? "READ" : "WRITE", (long)aio_event->offset, aio_event->nbytes);
SwooleG.running = 0;
}
#ifndef HAVE_DAEMON
int daemon(int nochdir, int noclose)
{
pid_t pid;
if (!nochdir && chdir("/") != 0)
{
swWarn("chdir() failed. Error: %s[%d]", strerror(errno), errno);
return -1;
}
if (!noclose)
{
int fd = open("/dev/null", O_RDWR);
if (fd < 0)
{
swWarn("open() failed. Error: %s[%d]", strerror(errno), errno);
return -1;
}
if (dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0)
{
close(fd);
swWarn("dup2() failed. Error: %s[%d]", strerror(errno), errno);
return -1;
}
close(fd);
}
pid = fork();
if (pid < 0)
{
swWarn("fork() failed. Error: %s[%d]", strerror(errno), errno);
return -1;
}
if (pid > 0)
{
_exit(0);
}
if (setsid() < 0)
{
swWarn("setsid() failed. Error: %s[%d]", strerror(errno), errno);
return -1;
}
return 0;
}
#endif
static int swAioBase_onFinish(swReactor *reactor, swEvent *event)
{
int i;
swAio_event *events[SW_AIO_EVENT_NUM];
int n = read(event->fd, events, sizeof(swAio_event*) * SW_AIO_EVENT_NUM);
if (n < 0)
{
swWarn("read() failed. Error: %s[%d]", strerror(errno), errno);
return SW_ERR;
}
for (i = 0; i < n / sizeof(swAio_event*); i++)
{
if (events[i]->callback)
{
events[i]->callback(events[i]);
}
else
{
SwooleAIO.callback(events[i]);
}
SwooleAIO.task_num--;
sw_free(events[i]);
}
return SW_OK;
}
int swAioBase_init(int max_aio_events)
{
if (swPipeBase_create(&swoole_aio_pipe, 0) < 0)
{
return SW_ERR;
}
if (swMutex_create(&SwooleAIO.lock, 0) < 0)
{
swWarn("create mutex lock error.");
return SW_ERR;
}
if (SwooleAIO.thread_num <= 0)
{
SwooleAIO.thread_num = SW_AIO_THREAD_NUM_DEFAULT;
}
if (swThreadPool_create(&swAioBase_thread_pool, SwooleAIO.thread_num) < 0)
{
return SW_ERR;
}
swAioBase_thread_pool.onTask = swAioBase_thread_onTask;
swAioBase_pipe_read = swoole_aio_pipe.getFd(&swoole_aio_pipe, 0);
swAioBase_pipe_write = swoole_aio_pipe.getFd(&swoole_aio_pipe, 1);
SwooleAIO.handlers[SW_AIO_READ] = swAio_handler_read;
SwooleAIO.handlers[SW_AIO_WRITE] = swAio_handler_write;
SwooleAIO.handlers[SW_AIO_GETHOSTBYNAME] = swAio_handler_gethostbyname;
SwooleAIO.handlers[SW_AIO_GETADDRINFO] = swAio_handler_getaddrinfo;
SwooleAIO.handlers[SW_AIO_STREAM_GET_LINE] = swAio_handler_stream_get_line;
SwooleAIO.handlers[SW_AIO_READ_FILE] = swAio_handler_read_file;
SwooleAIO.handlers[SW_AIO_WRITE_FILE] = swAio_handler_write_file;
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_AIO, swAioBase_onFinish);
SwooleG.main_reactor->add(SwooleG.main_reactor, swAioBase_pipe_read, SW_FD_AIO);
if (swThreadPool_run(&swAioBase_thread_pool) < 0)
{
return SW_ERR;
}
SwooleAIO.destroy = swAioBase_destroy;
SwooleAIO.read = swAioBase_read;
SwooleAIO.write = swAioBase_write;
SwooleAIO.init = 1;
return SW_OK;
}
static void swAio_handler_read(swAio_event *event)
{
int ret = -1;
if (flock(event->fd, LOCK_SH) < 0)
{
swSysError("flock(%d, LOCK_SH) failed.", event->fd);
event->ret = -1;
event->error = errno;
return;
}
while (1)
{
ret = pread(event->fd, event->buf, event->nbytes, event->offset);
if (ret < 0 && (errno == EINTR || errno == EAGAIN))
{
continue;
}
break;
}
if (flock(event->fd, LOCK_UN) < 0)
{
swSysError("flock(%d, LOCK_UN) failed.", event->fd);
}
event->ret = ret;
}
static inline char* find_eol(char *buf, size_t size)
{
char *eol = memchr(buf, '\n', size);
if (!eol)
{
eol = memchr(buf, '\r', size);
}
return eol;
}
static void swAio_handler_stream_get_line(swAio_event *event)
{
int ret = -1;
if (flock(event->fd, LOCK_SH) < 0)
{
swSysError("flock(%d, LOCK_SH) failed.", event->fd);
event->ret = -1;
event->error = errno;
return;
}
off_t readpos = event->offset;
off_t writepos = (long) event->req;
size_t avail = 0;
char *eol;
char *tmp;
char *read_buf = event->buf;
int read_n = event->nbytes;
while (1)
{
avail = writepos - readpos;
swTraceLog(SW_TRACE_AIO, "readpos=%ld, writepos=%ld", (long)readpos, (long)writepos);
if (avail > 0)
{
tmp = event->buf + readpos;
eol = find_eol(tmp, avail);
if (eol)
{
event->buf = tmp;
event->ret = (eol - tmp) + 1;
readpos += event->ret;
goto _return;
}
else if (readpos == 0)
{
if (writepos == event->nbytes)
{
writepos = 0;
event->ret = event->nbytes;
goto _return;
}
else
{
event->flags = SW_AIO_EOF;
((char*) event->buf)[writepos] = '\0';
event->ret = writepos;
writepos = 0;
goto _return;
}
}
else
{
memmove(event->buf, event->buf + readpos, avail);
writepos = avail;
read_buf = event->buf + writepos;
read_n = event->nbytes - writepos;
readpos = 0;
goto _readfile;
}
}
else
{
_readfile: while (1)
{
ret = read(event->fd, read_buf, read_n);
if (ret < 0 && (errno == EINTR || errno == EAGAIN))
{
continue;
}
break;
}
if (ret > 0)
{
writepos += ret;
}
else if (ret == 0)
{
event->flags = SW_AIO_EOF;
if (writepos > 0)
{
event->ret = writepos;
}
else
{
((char*) event->buf)[0] = '\0';
event->ret = 0;
}
readpos = writepos = 0;
goto _return;
}
}
}
_return:
if (flock(event->fd, LOCK_UN) < 0)
{
swSysError("flock(%d, LOCK_UN) failed.", event->fd);
}
event->offset = readpos;
event->req = (void *) (long) writepos;
}
static void swAio_handler_read_file(swAio_event *event)
{
int ret = -1;
int fd = open(event->req, O_RDONLY);
if (fd < 0)
{
swSysError("open(%s, O_RDONLY) failed.", (char * )event->req);
event->ret = ret;
event->error = errno;
return;
}
struct stat file_stat;
if (fstat(fd, &file_stat) < 0)
{
swSysError("fstat(%s) failed.", (char * )event->req);
_error: close(fd);
event->ret = ret;
event->error = errno;
return;
}
if ((file_stat.st_mode & S_IFMT) != S_IFREG)
{
errno = EISDIR;
goto _error;
}
long filesize = file_stat.st_size;
if (filesize == 0)
{
errno = SW_ERROR_FILE_EMPTY;
goto _error;
}
if (flock(fd, LOCK_SH) < 0)
{
swSysError("flock(%d, LOCK_SH) failed.", event->fd);
goto _error;
}
event->buf = sw_malloc(filesize);
if (event->buf == NULL)
{
goto _error;
}
int readn = swoole_sync_readfile(fd, event->buf, (int) filesize);
if (flock(fd, LOCK_UN) < 0)
{
swSysError("flock(%d, LOCK_UN) failed.", event->fd);
}
close(fd);
event->ret = readn;
event->error = 0;
}
static void swAio_handler_write_file(swAio_event *event)
{
int ret = -1;
int fd = open(event->req, event->flags, 0644);
if (fd < 0)
{
swSysError("open(%s, %d) failed.", (char * )event->req, event->flags);
event->ret = ret;
event->error = errno;
return;
}
if (flock(fd, LOCK_EX) < 0)
{
swSysError("flock(%d, LOCK_EX) failed.", event->fd);
event->ret = ret;
event->error = errno;
close(fd);
return;
}
int written = swoole_sync_writefile(fd, event->buf, event->nbytes);
if (event->flags & SW_AIO_WRITE_FSYNC)
{
if (fsync(fd) < 0)
{
swSysError("fsync(%d) failed.", event->fd);
}
}
if (flock(fd, LOCK_UN) < 0)
{
swSysError("flock(%d, LOCK_UN) failed.", event->fd);
}
close(fd);
event->ret = written;
event->error = 0;
}
static void swAio_handler_write(swAio_event *event)
{
int ret = -1;
if (flock(event->fd, LOCK_EX) < 0)
{
swSysError("flock(%d, LOCK_EX) failed.", event->fd);
return;
}
if (event->offset == 0)
{
ret = write(event->fd, event->buf, event->nbytes);
}
else
{
ret = pwrite(event->fd, event->buf, event->nbytes, event->offset);
}
if (event->flags & SW_AIO_WRITE_FSYNC)
{
if (fsync(event->fd) < 0)
{
swSysError("fsync(%d) failed.", event->fd);
}
}
if (flock(event->fd, LOCK_UN) < 0)
{
swSysError("flock(%d, LOCK_UN) failed.", event->fd);
}
event->ret = ret;
}
static void swAio_handler_gethostbyname(swAio_event *event)
{
struct in_addr addr_v4;
struct in6_addr addr_v6;
int ret;
#ifndef HAVE_GETHOSTBYNAME2_R
SwooleAIO.lock.lock(&SwooleAIO.lock);
#endif
if (event->flags == AF_INET6)
{
ret = swoole_gethostbyname(AF_INET6, event->buf, (char *) &addr_v6);
}
else
{
ret = swoole_gethostbyname(AF_INET, event->buf, (char *) &addr_v4);
}
bzero(event->buf, event->nbytes);
#ifndef HAVE_GETHOSTBYNAME2_R
SwooleAIO.lock.unlock(&SwooleAIO.lock);
#endif
if (ret < 0)
{
event->error = h_errno;
}
else
{
if (inet_ntop(event->flags == AF_INET6 ? AF_INET6 : AF_INET,
event->flags == AF_INET6 ? (void *) &addr_v6 : (void *) &addr_v4, event->buf, event->nbytes) == NULL)
{
ret = -1;
event->error = SW_ERROR_BAD_IPV6_ADDRESS;
}
else
{
event->error = 0;
ret = 0;
}
}
event->ret = ret;
}
static void swAio_handler_getaddrinfo(swAio_event *event)
{
swRequest_getaddrinfo *req = (swRequest_getaddrinfo *) event->req;
event->ret = swoole_getaddrinfo(req);
event->error = req->error;
}
static int swAioBase_thread_onTask(swThreadPool *pool, void *task, int task_len)
{
swAio_event *event = task;
if (event->type >= SW_AIO_HANDLER_MAX_SIZE || SwooleAIO.handlers[event->type] == NULL)
{
event->error = SW_ERROR_AIO_BAD_REQUEST;
event->ret = -1;
goto _error;
}
SwooleAIO.handlers[event->type](event);
swTrace("aio_thread ok. ret=%d, error=%d", event->ret, event->error);
_error: do
{
SwooleAIO.lock.lock(&SwooleAIO.lock);
int ret = write(swAioBase_pipe_write, &task, sizeof(task));
SwooleAIO.lock.unlock(&SwooleAIO.lock);
if (ret < 0)
{
if (errno == EAGAIN)
{
swYield();
continue;
}
else if (errno == EINTR)
{
continue;
}
else
{
swSysError("sendto swoole_aio_pipe_write failed.");
}
}
break;
} while (1);
return SW_OK;
}
static int swAioBase_write(int fd, void *inbuf, size_t size, off_t offset)
{
swAio_event *aio_ev = (swAio_event *) sw_malloc(sizeof(swAio_event));
if (aio_ev == NULL)
{
swWarn("malloc failed.");
return SW_ERR;
}
bzero(aio_ev, sizeof(swAio_event));
aio_ev->fd = fd;
aio_ev->buf = inbuf;
aio_ev->type = SW_AIO_WRITE;
aio_ev->nbytes = size;
aio_ev->offset = offset;
aio_ev->task_id = SwooleAIO.current_id++;
if (swThreadPool_dispatch(&swAioBase_thread_pool, aio_ev, sizeof(aio_ev)) < 0)
{
return SW_ERR;
}
else
{
SwooleAIO.task_num++;
return aio_ev->task_id;
}
}
int swAio_dns_lookup(void *hostname, void *ip_addr, size_t size)
{
swAio_event *aio_ev = (swAio_event *) sw_malloc(sizeof(swAio_event));
if (aio_ev == NULL)
{
swWarn("malloc failed.");
return SW_ERR;
}
bzero(aio_ev, sizeof(swAio_event));
aio_ev->buf = ip_addr;
aio_ev->req = hostname;
aio_ev->type = SW_AIO_GETHOSTBYNAME;
aio_ev->nbytes = size;
aio_ev->task_id = SwooleAIO.current_id++;
if (swThreadPool_dispatch(&swAioBase_thread_pool, aio_ev, sizeof(aio_ev)) < 0)
{
return SW_ERR;
}
else
{
SwooleAIO.task_num++;
return aio_ev->task_id;
}
}
int swAio_dispatch(swAio_event *_event)
{
if (SwooleAIO.init == 0)
{
swAio_init();
}
_event->task_id = SwooleAIO.current_id++;
swAio_event *event = (swAio_event *) sw_malloc(sizeof(swAio_event));
if (event == NULL)
{
swWarn("malloc failed.");
return SW_ERR;
}
memcpy(event, _event, sizeof(swAio_event));
if (swThreadPool_dispatch(&swAioBase_thread_pool, event, sizeof(event)) < 0)
{
return SW_ERR;
}
else
{
SwooleAIO.task_num++;
return _event->task_id;
}
}
static int swAioBase_read(int fd, void *inbuf, size_t size, off_t offset)
{
swAio_event *aio_ev = (swAio_event *) sw_malloc(sizeof(swAio_event));
if (aio_ev == NULL)
{
swWarn("malloc failed.");
return SW_ERR;
}
bzero(aio_ev, sizeof(swAio_event));
aio_ev->fd = fd;
aio_ev->buf = inbuf;
aio_ev->type = SW_AIO_READ;
aio_ev->nbytes = size;
aio_ev->offset = offset;
aio_ev->task_id = SwooleAIO.current_id++;
if (swThreadPool_dispatch(&swAioBase_thread_pool, aio_ev, sizeof(aio_ev)) < 0)
{
return SW_ERR;
}
else
{
SwooleAIO.task_num++;
return aio_ev->task_id;
}
}
void swAioBase_destroy()
{
swThreadPool_free(&swAioBase_thread_pool);
if (SwooleG.main_reactor)
{
SwooleG.main_reactor->del(SwooleG.main_reactor, swAioBase_pipe_read);
}
swoole_aio_pipe.close(&swoole_aio_pipe);
}