#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/time.h>
#include <liburing.h>
static unsigned long long mtime_since(const struct timeval *s,
const struct timeval *e)
{
long long sec, usec;
sec = e->tv_sec - s->tv_sec;
usec = (e->tv_usec - s->tv_usec);
if (sec > 0 && usec < 0) {
sec--;
usec += 1000000;
}
sec *= 1000;
usec /= 1000;
return sec + usec;
}
static unsigned long long mtime_since_now(struct timeval *tv)
{
struct timeval end;
gettimeofday(&end, NULL);
return mtime_since(tv, &end);
}
static int register_memory(struct io_uring *ring, void *ptr, size_t size)
{
struct io_uring_region_desc rd = {};
struct io_uring_mem_region_reg mr = {};
rd.user_addr = (__u64)(unsigned long)ptr;
rd.size = size;
rd.flags = IORING_MEM_REGION_TYPE_USER;
mr.region_uptr = (__u64)(unsigned long)&rd;
mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
return io_uring_register_region(ring, &mr);
}
int main(int argc, char *argv[])
{
struct io_uring_reg_wait *reg;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe[2];
struct io_uring ring;
char b1[8], b2[8];
unsigned long msec;
struct timeval tv;
int ret, fds[2];
int page_size;
if (argc > 1) {
fprintf(stdout, "%s: takes no arguments\n", argv[0]);
return 0;
}
page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
fprintf(stderr, "sysconf(_SC_PAGESIZE) failed\n");
return 1;
}
if (pipe(fds) < 0) {
perror("pipe");
return 1;
}
ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
if (ret) {
fprintf(stderr, "Queue init: %d\n", ret);
return 1;
}
reg = aligned_alloc(page_size, page_size);
if (!reg) {
fprintf(stderr, "allocation failed\n");
return 1;
}
ret = register_memory(&ring, reg, page_size);
if (ret) {
if (ret == -EINVAL) {
fprintf(stderr, "Kernel doesn't support registered waits\n");
return 1;
}
fprintf(stderr, "Registered wait: %d\n", ret);
return 1;
}
ret = io_uring_enable_rings(&ring);
if (ret) {
fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
return 1;
}
reg[0].ts.tv_sec = 1;
reg[0].ts.tv_nsec = 0;
reg[0].flags = IORING_REG_WAIT_TS;
reg[1].ts.tv_sec = 0;
reg[1].ts.tv_nsec = 100000000LL;
reg[1].min_wait_usec = 10000;
reg[1].flags = IORING_REG_WAIT_TS;
gettimeofday(&tv, NULL);
ret = io_uring_submit_and_wait_reg(&ring, cqe, 1, 0);
if (ret == -EINVAL) {
fprintf(stderr, "Kernel doesn't support registered waits\n");
return 1;
} else if (ret != -ETIME) {
fprintf(stderr, "Wait should've timed out... %d\n", ret);
return 1;
}
msec = mtime_since_now(&tv);
if (msec < 900 || msec > 1100) {
fprintf(stderr, "Wait took an unexpected amount of time: %lu\n",
msec);
return 1;
}
sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fds[0], b1, sizeof(b1), 0);
sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fds[0], b2, sizeof(b2), 0);
ret = write(fds[1], "Hello", 5);
if (ret < 0) {
perror("write");
return 1;
}
gettimeofday(&tv, NULL);
ret = io_uring_submit_and_wait_reg(&ring, cqe, 2, 1);
msec = mtime_since_now(&tv);
if (ret != 2) {
fprintf(stderr, "Should have submitted 2: %d\n", ret);
return 1;
}
if (msec < 8 || msec > 12)
fprintf(stderr, "min_wait_usec should take ~10 msec: %lu\n", msec);
io_uring_queue_exit(&ring);
free(reg);
return 0;
}