aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/lib/compat/thread.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-10 21:51:57 +0300
committerGitHub <noreply@github.com>2026-09-10 21:51:57 +0300
commit425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch)
treee52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/lib/compat/thread.zig
parent90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff)
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/lib/compat/thread.zig')
-rw-r--r--vendor/libghostty-vt/src/lib/compat/thread.zig75
1 files changed, 75 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/lib/compat/thread.zig b/vendor/libghostty-vt/src/lib/compat/thread.zig
new file mode 100644
index 00000000..890c02f0
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/thread.zig
@@ -0,0 +1,75 @@
+//! Code taken from 0.17.0 `std.Io.Condition`. See README.md for license and
+//! details.
+//!
+//! FIXME: Should be able to remove after 0.17.0, see:
+//! https://codeberg.org/ziglang/zig/pulls/31278
+const builtin = @import("builtin");
+const std = @import("std");
+const assert = @import("../../quirks.zig").inlineAssert;
+
+pub const WaitTimeoutError = std.Io.Cancelable || std.Io.Timeout.Error;
+
+/// Blocks until the condition is signaled, canceled, or the provided
+/// timeout expires.
+///
+/// See also:
+/// * `wait`
+/// * `waitUncancelable`
+pub fn waitTimeout(
+ cond: *std.Io.Condition,
+ io: std.Io,
+ mutex: *std.Io.Mutex,
+ timeout: std.Io.Timeout,
+) WaitTimeoutError!void {
+ const deadline = timeout.toDeadline(io);
+
+ var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load
+
+ {
+ const prev_state = cond.state.fetchAdd(.{ .waiters = 1, .signals = 0 }, .monotonic);
+ assert(prev_state.waiters < std.math.maxInt(u16)); // overflow caused by too many waiters
+ }
+
+ mutex.unlock(io);
+ defer mutex.lockUncancelable(io);
+
+ while (true) {
+ const result = io.futexWaitTimeout(u32, &cond.epoch.raw, epoch, deadline);
+
+ epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` load
+
+ // Even on error, try to consume a pending signal first. Otherwise a race might
+ // cause a signal to get stuck in the state with no corresponding waiter.
+ {
+ var prev_state = cond.state.load(.monotonic);
+ while (prev_state.signals > 0) {
+ prev_state = cond.state.cmpxchgWeak(prev_state, .{
+ .waiters = prev_state.waiters - 1,
+ .signals = prev_state.signals - 1,
+ }, .acquire, .monotonic) orelse {
+ // We successfully consumed a signal.
+ return;
+ };
+ }
+ }
+
+ // There are no more signals available; this was a spurious wakeup or an error. If it
+ // was an error, we will remove ourselves as a waiter and return that error. If a
+ // timeout was specified and the deadline has passed, we remove ourselves as a waiter
+ // and return `error.Timeout`. Otherwise, we'll loop back to the futex wait.
+ result catch |err| {
+ const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic);
+ assert(prev_state.waiters > 0); // underflow caused by illegal state
+ return err;
+ };
+ switch (deadline) {
+ .none => {},
+ .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) {
+ const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic);
+ assert(prev_state.waiters > 0); // underflow caused by illegal state
+ return error.Timeout;
+ },
+ .duration => unreachable,
+ }
+ }
+}