mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
wip: io-uring
This commit is contained in:
parent
9b0d5e7c96
commit
ae8394b0c1
5 changed files with 250 additions and 9 deletions
15
meson.build
15
meson.build
|
|
@ -140,6 +140,7 @@ wayland_cursor = dependency('wayland-cursor')
|
|||
xkb = dependency('xkbcommon', version: '>=1.8.0')
|
||||
fontconfig = dependency('fontconfig')
|
||||
utf8proc = dependency('libutf8proc', required: get_option('grapheme-clustering'))
|
||||
io_uring = dependency('liburing', required: get_option('io-uring'))
|
||||
|
||||
if utf8proc.found()
|
||||
add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c')
|
||||
|
|
@ -149,6 +150,10 @@ if pixman.version().version_compare('>=0.46.0')
|
|||
add_project_arguments('-DHAVE_PIXMAN_RGBA_16', language: 'c')
|
||||
endif
|
||||
|
||||
if io_uring.found()
|
||||
add_project_arguments('-DFOOT_IO_URING=1', language: 'c')
|
||||
endif
|
||||
|
||||
tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist')
|
||||
fcft = dependency('fcft', version: ['>=3.3.1', '<4.0.0'], fallback: 'fcft')
|
||||
|
||||
|
|
@ -260,7 +265,7 @@ vtlib = static_library(
|
|||
builtin_terminfo, srgb_funcs,
|
||||
wl_proto_src + wl_proto_headers,
|
||||
version,
|
||||
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc],
|
||||
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc, io_uring],
|
||||
link_with: [common, misc],
|
||||
)
|
||||
|
||||
|
|
@ -271,7 +276,7 @@ pgolib = static_library(
|
|||
'terminal.c', 'terminal.h',
|
||||
emoji_variation_sequences,
|
||||
wl_proto_src + wl_proto_headers,
|
||||
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc],
|
||||
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc, io_uring],
|
||||
link_with: vtlib,
|
||||
)
|
||||
|
||||
|
|
@ -287,7 +292,7 @@ if get_option('b_pgo') == 'generate'
|
|||
'pgo',
|
||||
'pgo/pgo.c',
|
||||
wl_proto_src + wl_proto_headers,
|
||||
dependencies: [math, threads, libepoll, pixman, wayland_client, xkb, utf8proc, fcft, tllist],
|
||||
dependencies: [math, threads, libepoll, pixman, wayland_client, xkb, utf8proc, fcft, tllist, io_uring],
|
||||
link_with: pgolib,
|
||||
)
|
||||
endif
|
||||
|
|
@ -320,8 +325,7 @@ executable(
|
|||
'user-notification.c', 'user-notification.h',
|
||||
'wayland.c', 'wayland.h', 'shm-formats.h',
|
||||
srgb_funcs, wl_proto_src + wl_proto_headers, version,
|
||||
dependencies: [math, threads, libepoll, pixman, wayland_client, wayland_cursor, xkb, fontconfig, utf8proc,
|
||||
tllist, fcft],
|
||||
dependencies: [math, threads, libepoll, pixman, wayland_client, wayland_cursor, xkb, fontconfig, utf8proc, io_uring, tllist, fcft],
|
||||
link_with: pgolib,
|
||||
install: true)
|
||||
|
||||
|
|
@ -422,6 +426,7 @@ summary(
|
|||
'Grapheme clustering': utf8proc.found(),
|
||||
'utmp backend': utmp_backend,
|
||||
'utmp helper default path': utmp_default_helper_path,
|
||||
'io-uring': io_uring.found(),
|
||||
'Build terminfo': tic.found(),
|
||||
'Terminfo base name': terminfo_base_name,
|
||||
'Terminfo install location': terminfo_install_location,
|
||||
|
|
|
|||
|
|
@ -27,3 +27,5 @@ option('utmp-backend', type: 'combo', value: 'auto', choices: ['none', 'libutemp
|
|||
description: 'Which utmp logging backend to use. This affects how (with what arguments) the utmp helper binary (see \'utmp-default-helper-path\')is called. Default: auto (linux=libutempter, freebsd=ulog, others=none)')
|
||||
option('utmp-default-helper-path', type: 'string', value: 'auto',
|
||||
description: 'Default path to the utmp helper binary. Default: auto-detect')
|
||||
|
||||
option('io-uring', type: 'feature', value: 'enabled', description: 'Build with io-uring support')
|
||||
|
|
|
|||
|
|
@ -1855,7 +1855,7 @@ send_clipboard_or_primary(struct seat *seat, int fd, const char *selection,
|
|||
}
|
||||
|
||||
static void
|
||||
send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
|
||||
selection_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
|
||||
int32_t fd)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
|
|
@ -1900,7 +1900,7 @@ action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action)
|
|||
|
||||
static const struct wl_data_source_listener data_source_listener = {
|
||||
.target = &target,
|
||||
.send = &send,
|
||||
.send = &selection_send,
|
||||
.cancelled = &cancelled,
|
||||
.dnd_drop_performed = &dnd_drop_performed,
|
||||
.dnd_finished = &dnd_finished,
|
||||
|
|
|
|||
226
terminal.c
226
terminal.c
|
|
@ -237,6 +237,152 @@ static bool cursor_blink_rearm_timer(struct terminal *term);
|
|||
|
||||
/* Externally visible, but not declared in terminal.h, to enable pgo
|
||||
* to call this function directly */
|
||||
|
||||
#if defined(FOOT_IO_URING)
|
||||
static bool
|
||||
fdm_ptmx_eventfd(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
|
||||
/* Prevent blinking while typing */
|
||||
if (term->cursor_blink.fd >= 0) {
|
||||
term->cursor_blink.state = CURSOR_BLINK_ON;
|
||||
cursor_blink_rearm_timer(term);
|
||||
}
|
||||
|
||||
if (unlikely(term->interactive_resizing.grid != NULL)) {
|
||||
/*
|
||||
* Don't consume PTMX while we're doing an interactive resize,
|
||||
* since the 'normal' grid we're currently using is a
|
||||
* temporary one - all changes done to it will be lost when
|
||||
* the interactive resize ends.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
//const size_t max_iterations = !hup ? 10 : SIZE_MAX;
|
||||
const size_t max_iterations = 100;
|
||||
|
||||
for (size_t i = 0; i < max_iterations; i++) {
|
||||
struct io_uring_cqe *cqe;
|
||||
int ret = io_uring_peek_cqe(&term->uring.ring, &cqe);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
if (-ret == EAGAIN)
|
||||
break;
|
||||
|
||||
LOG_ERRNO_P(-ret, "failed to get CQE from io-uring");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(cqe->flags & IORING_CQE_F_MORE)) {
|
||||
if (cqe->res >= 0 || -cqe->res == ENOBUFS) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&term->uring.ring);
|
||||
io_uring_prep_read_multishot(sqe, term->ptmx, 0, -1, 123);
|
||||
io_uring_submit(&term->uring.ring);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(cqe->res < 0)) {
|
||||
if (-cqe->res == EAGAIN || -cqe->res == EIO || -cqe->res == ENOBUFS) {
|
||||
/*
|
||||
* EAGAIN: no more to read - FDM will trigger us again
|
||||
* EIO: assume PTY was closed - we already have, or will get, a EPOLLHUP
|
||||
*/
|
||||
io_uring_cqe_seen(&term->uring.ring, cqe);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_ERRNO_P(-cqe->res, "failed to read from pseudo terminal");
|
||||
io_uring_cqe_seen(&term->uring.ring, cqe);
|
||||
return false;
|
||||
}
|
||||
|
||||
xassert(cqe->flags & IORING_CQE_F_BUFFER);
|
||||
|
||||
const int buffer_id = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
|
||||
const ssize_t count = cqe->res;
|
||||
const uint8_t *buf = term->uring.buffers[buffer_id];
|
||||
|
||||
xassert(term->interactive_resizing.grid == NULL);
|
||||
vt_from_slave(term, buf, count);
|
||||
|
||||
io_uring_buf_ring_add(term->uring.buf_ring, (void *)buf, 24 * 1024, buffer_id, io_uring_buf_ring_mask(128), 0);
|
||||
io_uring_buf_ring_cq_advance(&term->uring.ring, term->uring.buf_ring, 1);
|
||||
}
|
||||
|
||||
if (!term->render.app_sync_updates.enabled) {
|
||||
/*
|
||||
* We likely need to re-render. But, we don't want to do it
|
||||
* immediately. Often, a single client update is done through
|
||||
* multiple writes. This could lead to us rendering one frame with
|
||||
* "intermediate" state.
|
||||
*
|
||||
* For example, we might end up rendering a frame
|
||||
* where the client just erased a line, while in the
|
||||
* next frame, the client wrote to the same line. This
|
||||
* causes screen "flickering".
|
||||
*
|
||||
* Mitigate by always incuring a small delay before
|
||||
* rendering the next frame. This gives the client
|
||||
* some time to finish the operation (and thus gives
|
||||
* us time to receive the last writes before doing any
|
||||
* actual rendering).
|
||||
*
|
||||
* We incur this delay *every* time we receive
|
||||
* input. To ensure we don't delay rendering
|
||||
* indefinitely, we start a second timer that is only
|
||||
* reset when we render.
|
||||
*
|
||||
* Note that when the client is producing data at a
|
||||
* very high pace, we're rate limited by the wayland
|
||||
* compositor anyway. The delay we introduce here only
|
||||
* has any effect when the renderer is idle.
|
||||
*/
|
||||
uint64_t lower_ns = term->conf->tweak.delayed_render_lower_ns;
|
||||
uint64_t upper_ns = term->conf->tweak.delayed_render_upper_ns;
|
||||
|
||||
if (lower_ns > 0 && upper_ns > 0) {
|
||||
#if PTMX_TIMING
|
||||
struct timespec now;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
if (last.tv_sec > 0 || last.tv_nsec > 0) {
|
||||
struct timespec diff;
|
||||
|
||||
timespec_sub(&now, &last, &diff);
|
||||
LOG_INFO("waited %lds %ldns for more input",
|
||||
(long)diff.tv_sec, diff.tv_nsec);
|
||||
}
|
||||
last = now;
|
||||
#endif
|
||||
|
||||
xassert(lower_ns < 1000000000);
|
||||
xassert(upper_ns < 1000000000);
|
||||
xassert(upper_ns > lower_ns);
|
||||
|
||||
timerfd_settime(
|
||||
term->delayed_render_timer.lower_fd, 0,
|
||||
&(struct itimerspec){.it_value = {.tv_nsec = lower_ns}},
|
||||
NULL);
|
||||
|
||||
/* Second timeout - only reset when we render. Set to one
|
||||
* frame (assuming 60Hz) */
|
||||
if (!term->delayed_render_timer.is_armed) {
|
||||
timerfd_settime(
|
||||
term->delayed_render_timer.upper_fd, 0,
|
||||
&(struct itimerspec){.it_value = {.tv_nsec = upper_ns}},
|
||||
NULL);
|
||||
term->delayed_render_timer.is_armed = true;
|
||||
}
|
||||
} else
|
||||
render_refresh(term);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
|
|
@ -292,6 +438,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
|||
|
||||
xassert(term->interactive_resizing.grid == NULL);
|
||||
vt_from_slave(term, buf, count);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!term->render.app_sync_updates.enabled) {
|
||||
|
|
@ -1406,6 +1553,16 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.active_notifications = tll_init(),
|
||||
};
|
||||
|
||||
#if defined(FOOT_IO_URING)
|
||||
{
|
||||
int io_ret = io_uring_queue_init(2, &term->uring.ring, 0);
|
||||
if (io_ret < 0) {
|
||||
LOG_ERRNO_P(-io_ret, "failed to initialize io_uring queue");
|
||||
goto close_fds;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pixman_region32_init(&term->render.last_overlay_clip);
|
||||
|
||||
term_update_ascii_printer(term);
|
||||
|
|
@ -1503,7 +1660,39 @@ term_window_configured(struct terminal *term)
|
|||
/* Enable ptmx FDM callback */
|
||||
if (!term->shutdown.in_progress) {
|
||||
xassert(term->window->is_configured);
|
||||
|
||||
#if defined(FOOT_IO_URING)
|
||||
{
|
||||
const unsigned short bid = 123;
|
||||
const size_t total_size = 3 * 1024 * 1024;
|
||||
const size_t buf_size = 24 * 1024;
|
||||
const size_t buf_count = total_size / buf_size;
|
||||
xassert(buf_count * buf_size == total_size);
|
||||
|
||||
int ret;
|
||||
term->uring.buf_ring = io_uring_setup_buf_ring(
|
||||
&term->uring.ring, buf_count, bid, 0, &ret);
|
||||
|
||||
term->uring.buffers = xmalloc(buf_count * sizeof(term->uring.buffers[0]));
|
||||
for (unsigned short id = 0; id < buf_count; id++) {
|
||||
void *buffer = xmalloc(buf_size);
|
||||
io_uring_buf_ring_add(
|
||||
term->uring.buf_ring, buffer, buf_size, id,
|
||||
io_uring_buf_ring_mask(buf_count), id);
|
||||
term->uring.buffers[id] = buffer;
|
||||
}
|
||||
io_uring_buf_ring_advance(term->uring.buf_ring, buf_count);
|
||||
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&term->uring.ring);
|
||||
io_uring_prep_read_multishot(sqe, term->ptmx, 0, -1, bid);
|
||||
io_uring_submit(&term->uring.ring);
|
||||
}
|
||||
|
||||
fdm_add(term->fdm, term->uring.ring.enter_ring_fd, EPOLLIN, &fdm_ptmx_eventfd, term);
|
||||
fdm_add(term->fdm, term->ptmx, 0, &fdm_ptmx, term);
|
||||
#else
|
||||
fdm_add(term->fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term);
|
||||
#endif
|
||||
|
||||
const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf);
|
||||
LOG_INFO("gamma-correct blending: %s", gamma_correct ? "enabled" : "disabled");
|
||||
|
|
@ -1723,9 +1912,10 @@ term_shutdown(struct terminal *term)
|
|||
|
||||
del_utmp_record(term->conf, term->reaper, term->ptmx);
|
||||
|
||||
if (term->window != NULL && term->window->is_configured)
|
||||
if (term->window != NULL && term->window->is_configured) {
|
||||
fdm_del(term->fdm, term->ptmx);
|
||||
else
|
||||
//fdm_del(term->fdm, term->io_uring.enter_ring_fd);
|
||||
} else
|
||||
close(term->ptmx);
|
||||
|
||||
if (!term->shutdown.client_has_terminated) {
|
||||
|
|
@ -1832,6 +2022,38 @@ term_destroy(struct terminal *term)
|
|||
if (term->shutdown.terminate_timeout_fd >= 0)
|
||||
fdm_del(term->fdm, term->shutdown.terminate_timeout_fd);
|
||||
|
||||
#if defined(FOOT_IO_URING)
|
||||
fdm_del(term->fdm, term->uring.ring.enter_ring_fd);
|
||||
|
||||
{
|
||||
/* Cancel all pending io-uring requests */
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&term->uring.ring);
|
||||
io_uring_prep_cancel(sqe, NULL, IORING_ASYNC_CANCEL_ALL | IORING_ASYNC_CANCEL_ANY);
|
||||
io_uring_submit(&term->uring.ring);
|
||||
|
||||
while (true) {
|
||||
struct io_uring_cqe *cqe;
|
||||
int ret = io_uring_peek_cqe(&term->uring.ring, &cqe);
|
||||
|
||||
if (ret < 0) {
|
||||
if (-ret != EAGAIN)
|
||||
LOG_ERRNO_P(-ret, "failed to pull CQE from io-uring");
|
||||
break;
|
||||
}
|
||||
|
||||
io_uring_cqe_seen(&term->uring.ring, cqe);
|
||||
}
|
||||
|
||||
/* Shutdown io-uring */
|
||||
io_uring_queue_exit(&term->uring.ring);
|
||||
|
||||
/* Free io-uring buffers */
|
||||
for (size_t i = 0; i < 128; i++)
|
||||
free(term->uring.buffers[i]);
|
||||
free(term->uring.buffers);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (term->window != NULL) {
|
||||
wayl_win_destroy(term->window);
|
||||
term->window = NULL;
|
||||
|
|
|
|||
12
terminal.h
12
terminal.h
|
|
@ -11,6 +11,10 @@
|
|||
#include <utf8proc.h>
|
||||
#endif
|
||||
|
||||
#if defined(FOOT_IO_URING)
|
||||
#include <liburing.h>
|
||||
#endif
|
||||
|
||||
#include <tllist.h>
|
||||
#include <fcft/fcft.h>
|
||||
|
||||
|
|
@ -428,6 +432,14 @@ struct terminal {
|
|||
pid_t slave;
|
||||
int ptmx;
|
||||
|
||||
#if defined(FOOT_IO_URING)
|
||||
struct {
|
||||
struct io_uring ring;
|
||||
struct io_uring_buf_ring *buf_ring;
|
||||
uint8_t **buffers;
|
||||
} uring;
|
||||
#endif
|
||||
|
||||
struct vt vt;
|
||||
struct grid *grid;
|
||||
struct grid normal;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue