blob: a7cfd442b268baaeeb36e5b4b60f963f37cddf42 [file] [log] [blame]
/*
* gcc -Wall -O2 -D_GNU_SOURCE -o ucontext-cp ucontext-cp.c -luring
*/
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <ucontext.h>
#include <signal.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "liburing.h"
#define QD 64
#define BS (32*1024)
typedef struct {
struct io_uring ring;
unsigned char stack_buf[SIGSTKSZ];
ucontext_t ctx_main, ctx_fnew;
} async_context;
typedef struct {
async_context *pctx;
int ret;
int infd;
int outfd;
} arguments_bundle;
#define DEFINE_AWAIT_OP(operation) \
static ssize_t await_##operation( \
async_context *pctx, \
int fd, \
const struct iovec *ioves, \
unsigned int nr_vecs, \
off_t offset) \
{ \
struct io_uring_sqe *sqe = io_uring_get_sqe(&pctx->ring); \
struct io_uring_cqe *cqe; \
\
if (!sqe) \
return -1; \
\
io_uring_prep_##operation(sqe, fd, ioves, nr_vecs, offset); \
io_uring_sqe_set_data(sqe, pctx); \
io_uring_submit(&pctx->ring); \
swapcontext(&pctx->ctx_fnew, &pctx->ctx_main); \
if (io_uring_peek_cqe(&pctx->ring, &cqe) < 0) \
return -1; \
io_uring_cqe_seen(&pctx->ring, cqe); \
\
return cqe->res; \
}
DEFINE_AWAIT_OP(readv)
DEFINE_AWAIT_OP(writev)
#undef DEFINE_AWAIT_OP
static int setup_context(unsigned entries, async_context *pctx)
{
int ret;
ret = io_uring_queue_init(entries, &pctx->ring, 0);
if (ret < 0) {
fprintf(stderr, "queue_init: %s\n", strerror(-ret));
return -1;
}
ret = getcontext(&pctx->ctx_fnew);
if (ret < 0) {
perror("getcontext");
return -1;
}
pctx->ctx_fnew.uc_stack.ss_sp = &pctx->stack_buf;
pctx->ctx_fnew.uc_stack.ss_size = sizeof(pctx->stack_buf);
pctx->ctx_fnew.uc_link = &pctx->ctx_main;
return 0;
}
static int copy_file(async_context *pctx, int infd, int outfd, struct iovec* piov)
{
off_t offset = 0;
for (;;) {
ssize_t bytes_read;
if ((bytes_read = await_readv(pctx, infd, piov, 1, offset)) < 0) {
perror("await_readv");
return 1;
}
if (bytes_read == 0)
return 0;
piov->iov_len = bytes_read;
if (await_writev(pctx, outfd, piov, 1, offset) != bytes_read) {
perror("await_writev");
return 1;
}
if (bytes_read < BS)
return 0;
offset += bytes_read;
}
}
static void copy_file_wrapper(arguments_bundle *pbundle) {
struct iovec iov = {
.iov_base = malloc(BS),
.iov_len = BS,
};
async_context *pctx = pbundle->pctx;
pbundle->ret = copy_file(pctx, pbundle->infd, pbundle->outfd, &iov);
free(iov.iov_base);
swapcontext(&pctx->ctx_fnew, &pctx->ctx_main);
}
int main(int argc, char *argv[])
{
async_context ctx;
int infd, outfd;
struct io_uring_cqe *cqe;
if (argc < 3) {
printf("%s: infile outfile\n", argv[0]);
return 1;
}
infd = open(argv[1], O_RDONLY);
if (infd < 0) {
perror("open infile");
return 1;
}
outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outfd < 0) {
perror("open outfile");
return 1;
}
if (setup_context(QD, &ctx))
return 1;
arguments_bundle bundle = {
.pctx = &ctx,
.ret = -1,
.infd = infd,
.outfd = outfd,
};
makecontext(&ctx.ctx_fnew, (void (*)(void)) copy_file_wrapper, 1, &bundle);
if (swapcontext(&ctx.ctx_main, &ctx.ctx_fnew)) {
perror("swapcontext");
return 1;
}
/* event loop */
while (bundle.ret == -1) {
int ret;
async_context* pctx;
/* usually be timed waiting */
ret = io_uring_wait_cqe(&ctx.ring, &cqe);
if (ret < 0) {
fprintf(stderr, "wait_cqe: %s\n", strerror(-ret));
return 1;
}
pctx = io_uring_cqe_get_data(cqe);
assert(pctx == &ctx);
if (swapcontext(&pctx->ctx_main, &pctx->ctx_fnew)) {
perror("swapcontext");
return 1;
}
}
close(outfd);
close(infd);
io_uring_queue_exit(&ctx.ring);
return 0;
}