diff mbox series

[2/6] api/evloop: Add helpers for creating an event loop

Message ID 20220927161408.23743-3-rpalethorpe@suse.com
State RFC
Headers show
Series Locally distributed work API | expand

Commit Message

Richard Palethorpe Sept. 27, 2022, 4:14 p.m. UTC
Puts some of the boiler plate for creating an "event loop", into an
API. Useful for asynchronous or evented I/O.

This uses epoll and signalfd which are very widely supported on
Linux. I also think epoll is a better interface than ppoll and
pselect.

The tst_epoll_event_data struct (added in the previous commit) can be
used to add callbacks on particular FD events.

There is also a special callback for the signlfd and on_cont which is
called at the end of each loop. Returning 0 from these will cause the
loop to exit.

Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com>
---
 include/tst_evloop.h |  32 ++++++++++++++
 lib/tst_evloop.c     | 102 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 134 insertions(+)
 create mode 100644 include/tst_evloop.h
 create mode 100644 lib/tst_evloop.c

Comments

Petr Vorel Sept. 30, 2022, 1:36 p.m. UTC | #1
Hi Richie,

> +++ b/lib/tst_evloop.c
...
> +void tst_evloop_run(struct tst_evloop *self)
> +{
> +	static int saturated_warn;
> +	const int maxevents = 128;
> +	struct epoll_event events[maxevents];
> +
> +	for (;;) {
> +		const int ev_num = SAFE_EPOLL_WAIT(self->epollfd, events,
> +						   maxevents, self->timeout);
> +
> +		for (int i = 0; i < ev_num; i++)
I'm sorry we still do not allow to use C99 due CentOS 7 still supported:
lib/tst_evloop.c:80:3: error: 'for' loop initial declarations are only allowed in C99 mode

Kind regards,
Petr
diff mbox series

Patch

diff --git a/include/tst_evloop.h b/include/tst_evloop.h
new file mode 100644
index 000000000..bdab2d6f7
--- /dev/null
+++ b/include/tst_evloop.h
@@ -0,0 +1,32 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 SUSE LLC <rpalethorpe@suse.com>
+ */
+
+#include "inttypes.h"
+#include "sys/signalfd.h"
+
+#include "tst_epoll.h"
+
+#ifndef TST_EVLOOP_H
+#define TST_EVLOOP_H
+
+struct tst_evloop {
+	int epollfd;
+	int signalfd;
+	struct tst_epoll_event_data signalfd_evdata;
+	int timeout;
+
+	void *priv;
+	int (*on_cont)(struct tst_evloop *self);
+	int (*on_signal)(struct tst_evloop *self, struct signalfd_siginfo *si);
+};
+
+void tst_evloop_setup(struct tst_evloop *self);
+void tst_evloop_run(struct tst_evloop *self);
+void tst_evloop_add(struct tst_evloop *self,
+		    struct tst_epoll_event_data *evdata,
+		    int fd, uint32_t events);
+void tst_evloop_cleanup(struct tst_evloop *self);
+
+#endif
diff --git a/lib/tst_evloop.c b/lib/tst_evloop.c
new file mode 100644
index 000000000..66d74ce58
--- /dev/null
+++ b/lib/tst_evloop.c
@@ -0,0 +1,102 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 SUSE LLC <rpalethorpe@suse.com>
+ */
+#define _GNU_SOURCE
+#define TST_NO_DEFAULT_MAIN
+
+#include "tst_test.h"
+#include "tst_evloop.h"
+
+static void handle_epoll_event(struct epoll_event *event)
+{
+	struct tst_epoll_event_data *data = event->data.ptr;
+
+	data->on_epoll(data->self, event->events);
+}
+
+static int evloop_on_signal(struct tst_evloop *self, uint32_t events)
+{
+	int i, n;
+	struct signalfd_siginfo si[16];
+
+	if (events ^ EPOLLIN) {
+		tst_brk(TBROK, "Unexpected event on signalfd");
+		return 1;
+	}
+
+	n = SAFE_READ(0, self->signalfd, si, sizeof(si));
+
+	if (!n)
+		tst_brk(TBROK, "Got EPOLLIN on signalfd, but no signal read from fd");
+
+	for (i = 0; i < n/(int)sizeof(si[0]); i++) {
+		if (!self->on_signal(self, si + i))
+			return 0;
+	}
+
+	return 1;
+}
+
+void tst_evloop_add(struct tst_evloop *self,
+		       struct tst_epoll_event_data *evdata,
+		       int fd, uint32_t events)
+{
+	struct epoll_event ev = {
+		.events = events,
+		.data.ptr = evdata,
+	};
+
+	SAFE_EPOLL_CTL(self->epollfd, EPOLL_CTL_ADD, fd, &ev);
+}
+
+void tst_evloop_setup(struct tst_evloop *self)
+{
+
+	sigset_t mask;
+
+	self->epollfd = SAFE_EPOLL_CREATE1(EPOLL_CLOEXEC);
+
+	sigfillset(&mask);
+	SAFE_SIGPROCMASK(SIG_BLOCK, &mask, NULL);
+	self->signalfd = signalfd(-1, &mask, SFD_CLOEXEC);
+
+	self->signalfd_evdata.self = self;
+	self->signalfd_evdata.on_epoll = (tst_on_epoll_fn)evloop_on_signal;
+
+	tst_evloop_add(self, &self->signalfd_evdata, self->signalfd, EPOLLIN);
+}
+
+void tst_evloop_run(struct tst_evloop *self)
+{
+	static int saturated_warn;
+	const int maxevents = 128;
+	struct epoll_event events[maxevents];
+
+	for (;;) {
+		const int ev_num = SAFE_EPOLL_WAIT(self->epollfd, events,
+						   maxevents, self->timeout);
+
+		for (int i = 0; i < ev_num; i++)
+			handle_epoll_event(events + i);
+
+		if (ev_num == maxevents) {
+			if (!saturated_warn)
+				tst_res(TINFO, "Event loop saturated");
+
+			saturated_warn = 1;
+			continue;
+		}
+
+		if (!self->on_cont(self))
+			break;
+	}
+}
+
+void tst_evloop_cleanup(struct tst_evloop *self)
+{
+	if (self->epollfd > 0)
+		SAFE_CLOSE(self->epollfd);
+	if (self->signalfd > 0)
+		SAFE_CLOSE(self->signalfd);
+}