summaryrefslogtreecommitdiff
path: root/lib/libc/aio/aio.c
blob: 0ca2ec64f19873537ca874af350f56f659d78626 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <__aio.h>
#include <io_uring.h>
#include <signal.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

struct aio_context __aio_context = { 0 };

// TODO
// static void *__aio_thread_notify(void *arg)
// {
// 	struct sigevent *sev = arg;
// 	sev->sigev_notify_function(sev->sigev_value);
// 	return NULL;
// }

static void __aio_deliver_signal(struct sigevent *sev)
{
	if (!sev)
		return;

	switch (sev->sigev_notify) {
	case SIGEV_SIGNAL:
		kill(getpid(), sev->sigev_signo);
		break;

	case SIGEV_THREAD: {
		// TODO
		// pthread_t thread;
		// pthread_create(&thread, NULL, __aio_thread_notify, sev);
		// pthread_detach(thread);
		break;
	}

	default:
		break;
	}
}

static void __aio_complete(struct aio_request *req, int res)
{
	req->result = res;
	req->status = AIO_REQUEST_STATUS_COMPLETED;

	if (req->grp) {
		if (res < 0)
			req->grp->error = 1;

		if (--req->grp->pending == 0) {
			uint64_t one = 1;
			write(req->grp->eventfd, &one, sizeof(one));

			if (req->grp->sig) {
				__aio_deliver_signal(req->grp->sig);
			}
		}
	}
}

int __aio_request(struct aio_request *req, int opcode)
{
	struct io_uring_sqe *sqe;
	unsigned index, tail;

	if (req == NULL)
		return -1;

	tail = *__io_uring.sq.tail;
	index = tail & *__io_uring.sq.ring_mask;

	sqe = &__io_uring.sq.sqes[index];
	memset(sqe, 0, sizeof(*sqe));
	sqe->opcode = opcode;
	sqe->fd = req->aiocbp->aio_fildes;
	sqe->addr = (uint64_t)req->aiocbp->aio_buf;
	sqe->len = req->aiocbp->aio_nbytes;
	sqe->off = req->aiocbp->aio_offset;
	sqe->user_data = (uint64_t)req;

	__io_uring.sq.array[index] = index;
	*__io_uring.sq.tail = tail + 1;

	io_uring_enter(__io_uring.fd, 1, 0, IORING_ENTER_GETEVENTS, NULL, 0);

	req->status = AIO_REQUEST_STATUS_PENDING;
	req->next = NULL;

	if (__aio_context.head == NULL) {
		__aio_context.head = __aio_context.tail = req;
	} else {
		__aio_context.tail->next = req;
		__aio_context.tail = req;
	}

	return 0;
}

void __aio_poll(void)
{
	unsigned head = *__io_uring.cq.head;
	unsigned tail = *__io_uring.cq.tail;

	while (head != tail) {
		struct io_uring_cqe *cqe =
			&__io_uring.cq.cqes[head & *__io_uring.cq.ring_mask];

		struct aio_request *req = (struct aio_request *)cqe->user_data;

		__aio_complete(req, cqe->res);

		head++;
	}

	*__io_uring.cq.head = head;
}

struct aio_request *__aio_lookup(const struct aiocb *aiocbp)
{
	struct aio_request *req = __aio_context.head;

	while (req != NULL) {
		if (req->aiocbp == aiocbp)
			return req;

		req = req->next;
	}

	return NULL;
}

struct aio_request *__aio_remove(const struct aiocb *aiocbp)
{
	struct aio_request **cur = &__aio_context.head;
	struct aio_request *req = NULL;

	while (*cur) {
		if ((*cur)->aiocbp == aiocbp) {
			req = *cur;
			*cur = req->next;
			if (__aio_context.tail == req)
				__aio_context.tail = NULL;
			break;
		}
		cur = &(*cur)->next;
	}

	return req;
}