mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Update libxev to use dynamic backend, support Linux configurability
Related to #3224 Previously, Ghostty used a static API for async event handling: io_uring on Linux, kqueue on macOS. This commit changes the backend to be dynamic on Linux so that epoll will be used if io_uring isn't available, or if the user explicitly chooses it. This introduces a new config `async-backend` (default "auto") which can be set by the user to change the async backend in use. This is a best-effort setting: if the user requests io_uring but it isn't available, Ghostty will fall back to something that is and that choice is up to us. Basic benchmarking both in libxev and Ghostty (vtebench) show no noticeable performance differences introducing the dynamic API, nor choosing epoll over io_uring.
This commit is contained in:
@ -7,8 +7,8 @@
|
||||
|
||||
.libxev = .{
|
||||
// mitchellh/libxev
|
||||
.url = "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz",
|
||||
.hash = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c",
|
||||
.url = "https://github.com/mitchellh/libxev/archive/8943932a668f338cb2c500f6e1a7396bacd8b55d.tar.gz",
|
||||
.hash = "1220a67b584c9499154de8c96851ed8e92315452cb2027c06e2d7d07a39c6f900d1a",
|
||||
},
|
||||
.mach_glfw = .{
|
||||
// mitchellh/mach-glfw
|
||||
|
6
build.zig.zon.nix
generated
6
build.zig.zon.nix
generated
@ -84,11 +84,11 @@ with lib; let
|
||||
in
|
||||
linkFarm name [
|
||||
{
|
||||
name = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c";
|
||||
name = "1220a67b584c9499154de8c96851ed8e92315452cb2027c06e2d7d07a39c6f900d1a";
|
||||
path = fetchZigArtifact {
|
||||
name = "libxev";
|
||||
url = "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz";
|
||||
hash = "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8=";
|
||||
url = "https://github.com/mitchellh/libxev/archive/8943932a668f338cb2c500f6e1a7396bacd8b55d.tar.gz";
|
||||
hash = "sha256-TGooUoby2J8PyzbdKHwdEXnu7f2g4T2/TUHj/ktBsN4=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
2
build.zig.zon.txt
generated
2
build.zig.zon.txt
generated
@ -10,7 +10,6 @@ https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fb
|
||||
https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz
|
||||
https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz
|
||||
https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz
|
||||
https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz
|
||||
https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz
|
||||
https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz
|
||||
https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz
|
||||
@ -32,6 +31,7 @@ https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b3
|
||||
https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/efb1bb1843500a751eb30afa58fe48a6bec8952c.tar.gz
|
||||
https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/8943932a668f338cb2c500f6e1a7396bacd8b55d.tar.gz
|
||||
https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz
|
||||
https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz
|
||||
https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz
|
||||
|
6
build.zig.zon2json-lock
generated
6
build.zig.zon2json-lock
generated
@ -1,8 +1,8 @@
|
||||
{
|
||||
"1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c": {
|
||||
"1220a67b584c9499154de8c96851ed8e92315452cb2027c06e2d7d07a39c6f900d1a": {
|
||||
"name": "libxev",
|
||||
"url": "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz",
|
||||
"hash": "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8="
|
||||
"url": "https://github.com/mitchellh/libxev/archive/8943932a668f338cb2c500f6e1a7396bacd8b55d.tar.gz",
|
||||
"hash": "sha256-TGooUoby2J8PyzbdKHwdEXnu7f2g4T2/TUHj/ktBsN4="
|
||||
},
|
||||
"12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62": {
|
||||
"name": "mach_glfw",
|
||||
|
@ -15,6 +15,7 @@ const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const builtin = @import("builtin");
|
||||
const build_config = @import("../../build_config.zig");
|
||||
const xev = @import("../../global.zig").xev;
|
||||
const build_options = @import("build_options");
|
||||
const apprt = @import("../../apprt.zig");
|
||||
const configpkg = @import("../../config.zig");
|
||||
@ -137,6 +138,27 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup our event loop backend
|
||||
if (config.@"async-backend" != .auto) {
|
||||
const result: bool = switch (config.@"async-backend") {
|
||||
.auto => unreachable,
|
||||
.epoll => xev.prefer(.epoll),
|
||||
.io_uring => xev.prefer(.io_uring),
|
||||
};
|
||||
|
||||
if (result) {
|
||||
log.info(
|
||||
"libxev manual backend={s}",
|
||||
.{@tagName(xev.backend)},
|
||||
);
|
||||
} else {
|
||||
log.warn(
|
||||
"libxev manual backend failed, using default={s}",
|
||||
.{@tagName(xev.backend)},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var gdk_debug: struct {
|
||||
/// output OpenGL debug information
|
||||
opengl: bool = false,
|
||||
|
@ -4,7 +4,7 @@ const Allocator = std.mem.Allocator;
|
||||
const builtin = @import("builtin");
|
||||
const build_config = @import("../build_config.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const renderer = @import("../renderer.zig");
|
||||
const gtk = if (build_config.app_runtime == .gtk) @import("../apprt/gtk/c.zig").c else void;
|
||||
|
||||
@ -37,7 +37,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
try stdout.print(" - app runtime: {}\n", .{build_config.app_runtime});
|
||||
try stdout.print(" - font engine: {}\n", .{build_config.font_backend});
|
||||
try stdout.print(" - renderer : {}\n", .{renderer.Renderer});
|
||||
try stdout.print(" - libxev : {}\n", .{xev.backend});
|
||||
try stdout.print(" - libxev : {s}\n", .{@tagName(xev.backend)});
|
||||
if (comptime build_config.app_runtime == .gtk) {
|
||||
try stdout.print(" - desktop env: {s}\n", .{@tagName(internal_os.desktopEnvironment())});
|
||||
try stdout.print(" - GTK version:\n", .{});
|
||||
|
@ -2235,6 +2235,34 @@ term: []const u8 = "xterm-ghostty",
|
||||
/// running. Defaults to an empty string if not set.
|
||||
@"enquiry-response": []const u8 = "",
|
||||
|
||||
/// Configures the low-level API to use for async IO, eventing, etc.
|
||||
///
|
||||
/// Most users should leave this set to `auto`. This will automatically detect
|
||||
/// scenarios where APIs may not be available (for example `io_uring` on
|
||||
/// certain hardened kernels) and fall back to a different API. However, if
|
||||
/// you want to force a specific backend for any reason, you can set this
|
||||
/// here.
|
||||
///
|
||||
/// Based on various benchmarks, we haven't found a statistically significant
|
||||
/// difference between the backends with regards to memory, CPU, or latency.
|
||||
/// The choice of backend is more about compatibility and features.
|
||||
///
|
||||
/// Available options:
|
||||
///
|
||||
/// * `auto` - Automatically choose the best backend for the platform
|
||||
/// based on available options.
|
||||
/// * `epoll` - Use the `epoll` API
|
||||
/// * `io_uring` - Use the `io_uring` API
|
||||
///
|
||||
/// If the selected backend is not available on the platform, Ghostty will
|
||||
/// fall back to an automatically chosen backend that is available.
|
||||
///
|
||||
/// Changing this value requires a full application restart to take effect.
|
||||
///
|
||||
/// This is only supported on Linux, since this is the only platform
|
||||
/// where we have multiple options. On macOS, we always use `kqueue`.
|
||||
@"async-backend": AsyncBackend = .auto,
|
||||
|
||||
/// Control the auto-update functionality of Ghostty. This is only supported
|
||||
/// on macOS currently, since Linux builds are distributed via package
|
||||
/// managers that are not centrally controlled by Ghostty.
|
||||
@ -5912,6 +5940,13 @@ pub const LinuxCgroup = enum {
|
||||
@"single-instance",
|
||||
};
|
||||
|
||||
/// See async-backend
|
||||
pub const AsyncBackend = enum {
|
||||
auto,
|
||||
epoll,
|
||||
io_uring,
|
||||
};
|
||||
|
||||
/// See auto-updates
|
||||
pub const AutoUpdate = enum {
|
||||
off,
|
||||
|
@ -9,7 +9,11 @@ const harfbuzz = @import("harfbuzz");
|
||||
const oni = @import("oniguruma");
|
||||
const crash = @import("crash/main.zig");
|
||||
const renderer = @import("renderer.zig");
|
||||
const xev = @import("xev");
|
||||
|
||||
/// We export the xev backend we want to use so that the rest of
|
||||
/// Ghostty can import this once and have access to the proper
|
||||
/// backend.
|
||||
pub const xev = @import("xev").Dynamic;
|
||||
|
||||
/// Global process state. This is initialized in main() for exe artifacts
|
||||
/// and by ghostty_init() for lib artifacts. This should ONLY be used by
|
||||
@ -114,6 +118,12 @@ pub const GlobalState = struct {
|
||||
// Setup our signal handlers before logging
|
||||
initSignals();
|
||||
|
||||
// Setup our Xev backend if we're dynamic
|
||||
if (comptime xev.dynamic) xev.detect() catch |err| {
|
||||
std.log.warn("failed to detect xev backend, falling back to " ++
|
||||
"most compatible backend err={}", .{err});
|
||||
};
|
||||
|
||||
// Output some debug information right away
|
||||
std.log.info("ghostty version={s}", .{build_config.version_string});
|
||||
std.log.info("ghostty build optimize={s}", .{build_config.mode_string});
|
||||
@ -126,7 +136,7 @@ pub const GlobalState = struct {
|
||||
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
|
||||
}
|
||||
std.log.info("renderer={}", .{renderer.Renderer});
|
||||
std.log.info("libxev backend={}", .{xev.backend});
|
||||
std.log.info("libxev default backend={s}", .{@tagName(xev.backend)});
|
||||
|
||||
// As early as possible, initialize our resource limits.
|
||||
self.rlimits = ResourceLimits.init();
|
||||
|
@ -13,7 +13,6 @@ const macos = @import("macos");
|
||||
const oni = @import("oniguruma");
|
||||
const cli = @import("cli.zig");
|
||||
const internal_os = @import("os/main.zig");
|
||||
const xev = @import("xev");
|
||||
const fontconfig = @import("fontconfig");
|
||||
const harfbuzz = @import("harfbuzz");
|
||||
const renderer = @import("renderer.zig");
|
||||
|
@ -6,9 +6,9 @@ pub const Thread = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const xev = @import("xev");
|
||||
const macos = @import("macos");
|
||||
|
||||
const xev = @import("../global.zig").xev;
|
||||
const BlockingQueue = @import("../datastruct/main.zig").BlockingQueue;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
@ -106,7 +106,7 @@ pub fn threadMain(self: *Thread) void {
|
||||
// If our loop is not stopped, then we need to keep running so that
|
||||
// messages are drained and we can wait for the surface to send a stop
|
||||
// message.
|
||||
if (!self.loop.flags.stopped) {
|
||||
if (!self.loop.stopped()) {
|
||||
log.warn("abrupt cf release thread exit detected, starting xev to drain mailbox", .{});
|
||||
defer log.debug("cf release thread fully exiting after abnormal failure", .{});
|
||||
self.flags.drain = true;
|
||||
|
@ -11,7 +11,7 @@ const objc = @import("objc");
|
||||
const macos = @import("macos");
|
||||
const imgui = @import("imgui");
|
||||
const glslang = @import("glslang");
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const apprt = @import("../apprt.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
const font = @import("../font/main.zig");
|
||||
|
@ -5,7 +5,7 @@ pub const Thread = @This();
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const crash = @import("../crash/main.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
|
@ -9,7 +9,7 @@ const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const posix = std.posix;
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const build_config = @import("../build_config.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
const crash = @import("../crash/main.zig");
|
||||
@ -589,7 +589,7 @@ fn ttyWrite(
|
||||
_: *xev.Completion,
|
||||
_: xev.Stream,
|
||||
_: xev.WriteBuffer,
|
||||
r: xev.Stream.WriteError!usize,
|
||||
r: xev.WriteError!usize,
|
||||
) xev.CallbackAction {
|
||||
const td = td_.?;
|
||||
td.write_req_pool.put();
|
||||
@ -634,13 +634,13 @@ pub const ThreadData = struct {
|
||||
|
||||
/// This is the pool of available (unused) write requests. If you grab
|
||||
/// one from the pool, you must put it back when you're done!
|
||||
write_req_pool: SegmentedPool(xev.Stream.WriteRequest, WRITE_REQ_PREALLOC) = .{},
|
||||
write_req_pool: SegmentedPool(xev.WriteRequest, WRITE_REQ_PREALLOC) = .{},
|
||||
|
||||
/// The pool of available buffers for writing to the pty.
|
||||
write_buf_pool: SegmentedPool([64]u8, WRITE_REQ_PREALLOC) = .{},
|
||||
|
||||
/// The write queue for the data stream.
|
||||
write_queue: xev.Stream.WriteQueue = .{},
|
||||
write_queue: xev.WriteQueue = .{},
|
||||
|
||||
/// This is used for both waiting for the process to exit and then
|
||||
/// subsequently to wait for the data_stream to close.
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! The options that are used to configure a terminal IO implementation.
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const apprt = @import("../apprt.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
const Command = @import("../Command.zig");
|
||||
|
@ -18,7 +18,7 @@ const Pty = @import("../pty.zig").Pty;
|
||||
const StreamHandler = @import("stream_handler.zig").StreamHandler;
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const terminfo = @import("../terminfo/main.zig");
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const renderer = @import("../renderer.zig");
|
||||
const apprt = @import("../apprt.zig");
|
||||
const fastmem = @import("../fastmem.zig");
|
||||
|
@ -14,7 +14,7 @@ pub const Thread = @This();
|
||||
const std = @import("std");
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const builtin = @import("builtin");
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const crash = @import("../crash/main.zig");
|
||||
const termio = @import("../termio.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
@ -189,7 +189,7 @@ pub fn threadMain(self: *Thread, io: *termio.Termio) void {
|
||||
// If our loop is not stopped, then we need to keep running so that
|
||||
// messages are drained and we can wait for the surface to send a stop
|
||||
// message.
|
||||
if (!self.loop.flags.stopped) {
|
||||
if (!self.loop.stopped()) {
|
||||
log.warn("abrupt io thread exit detected, starting xev to drain mailbox", .{});
|
||||
defer log.debug("io thread fully exiting after abnormal failure", .{});
|
||||
self.flags.drain = true;
|
||||
|
@ -3,7 +3,7 @@ const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const posix = std.posix;
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const build_config = @import("../build_config.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
|
@ -2,7 +2,7 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const renderer = @import("../renderer.zig");
|
||||
const termio = @import("../termio.zig");
|
||||
const BlockingQueue = @import("../datastruct/main.zig").BlockingQueue;
|
||||
|
@ -2,7 +2,7 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const xev = @import("xev");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const apprt = @import("../apprt.zig");
|
||||
const build_config = @import("../build_config.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
|
Reference in New Issue
Block a user