Kernel: Introduce a EINTR-only error type

This class should be used instead of Error in code paths shared between
kernel and user processes that cannot fail, but may be interrupted while
running in a user process. This generally includes code that may block
on a WaitQueue or a Mutex.
This commit is contained in:
Lucas Chollet authored and Sönke Holz committed 2026-09-03 19:21:56 +02:00
1 parent f330858208
commit 44658063c0
4 files changed
+43 -18

No files matched your search

+2 -6
View File
@@ -220,13 +220,9 @@ ErrorOr<NonnullOwnPtr<KBuffer>> FUSEDevice::send_request_and_wait_for_a_reply(Op
// Deal with that by just running the handler manually.
set_timed_out();
auto wait_status = m_instance_queue.wait_until(m_instances, receive_reply);
auto maybe_interrupted = m_instance_queue.wait_until(m_instances, receive_reply);
TimerQueue::the().cancel_timer(*timer);
if (wait_status.is_error()) {
VERIFY(wait_status.error().code() == EINTR);
return wait_status.release_error();
}
TRY(maybe_interrupted);
if (reply_or_error.is_error())
return reply_or_error.release_error();
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2026, Lucas Chollet <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Error.h>
namespace Kernel {
class [[nodiscard]] Interrupted {
public:
constexpr Interrupted() = default;
constexpr operator Error() const
{
return Error::from_errno(EINTR);
}
};
// This class should be used instead of ErrorOr in code paths shared between
// kernel and user processes that cannot fail, but may be interrupted while
// running in a user process. This generally includes code that may block on
// a WaitQueue or a Mutex.
template<typename T>
using InterruptedOr = ErrorOr<T, Interrupted>;
}
+3 -4
View File
@@ -48,16 +48,15 @@ void CadenceGEMNetworkAdapter::send_raw(ReadonlyBytes data)
auto* descriptor = tx_descriptor_entry(m_tx_enqueue_index);
ErrorOr<void> maybe_error {};
InterruptedOr<void> maybe_interrupted {};
do {
maybe_error = m_wait_queue.wait_until([&descriptor]() {
maybe_interrupted = m_wait_queue.wait_until([&descriptor]() {
// Inform the compiler that the contents of DMA regions might have changed,
// even if it didn't see any writes to them.
optimizer_fence();
return descriptor->is_software_owned;
});
} while (maybe_error.is_error() && maybe_error.error().code() == EINTR);
VERIFY(!maybe_error.is_error());
} while (maybe_interrupted.is_error());
// Ensure that the software owned check is done before writing the descriptor and buffer data.
full_memory_fence();
+9 -8
View File
@@ -9,6 +9,7 @@
#include <AK/IntrusiveList.h>
#include <AK/Optional.h>
#include <Kernel/Forward.h>
#include <Kernel/Library/Interrupted.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/Locking/SpinlockProtected.h>
#include <Kernel/Tasks/Thread.h>
@@ -20,20 +21,20 @@ class WaitQueue {
class Waiter {
public:
template<CallableAs<bool> F>
ErrorOr<void> wait_until(WaitQueue& wait_queue, F should_wake)
InterruptedOr<void> wait_until(WaitQueue& wait_queue, F should_wake)
{
// LockRank::None is just a dummy value.
return wait_until_impl<LockRank::None>(wait_queue, should_wake, OptionalNone {});
}
template<LockRank Rank, CallableAs<bool> F>
ErrorOr<void> wait_until(WaitQueue& wait_queue, Spinlock<Rank>& lock, F should_wake)
InterruptedOr<void> wait_until(WaitQueue& wait_queue, Spinlock<Rank>& lock, F should_wake)
{
return wait_until_impl(wait_queue, should_wake, lock);
}
template<typename T, LockRank Rank, CallableAs<bool, T&> F>
ErrorOr<void> wait_until(WaitQueue& wait_queue, SpinlockProtected<T, Rank>& spinlock_protected, F should_wake)
InterruptedOr<void> wait_until(WaitQueue& wait_queue, SpinlockProtected<T, Rank>& spinlock_protected, F should_wake)
{
return wait_until_impl<Rank>(wait_queue, [&spinlock_protected, &should_wake] { return should_wake(spinlock_protected.m_value); }, spinlock_protected.m_spinlock);
}
@@ -42,7 +43,7 @@ class WaitQueue {
private:
template<LockRank Rank, CallableAs<bool> F>
ErrorOr<void> wait_until_impl(WaitQueue& wait_queue, F should_wake, Optional<Spinlock<Rank>&> user_lock)
InterruptedOr<void> wait_until_impl(WaitQueue& wait_queue, F should_wake, Optional<Spinlock<Rank>&> user_lock)
{
bool was_interrupted = false;
@@ -87,7 +88,7 @@ class WaitQueue {
// but we still need to clear it.
Thread::current()->clear_interrupted();
if (was_interrupted) {
return EINTR;
return Interrupted {};
}
return {};
@@ -116,21 +117,21 @@ public:
void notify_one();
template<CallableAs<bool> F>
ErrorOr<void> wait_until(F should_wake)
InterruptedOr<void> wait_until(F should_wake)
{
Waiter waiter;
return waiter.wait_until(*this, move(should_wake));
}
template<LockRank Rank, CallableAs<bool> F>
ErrorOr<void> wait_until(Spinlock<Rank>& lock, F should_wake)
InterruptedOr<void> wait_until(Spinlock<Rank>& lock, F should_wake)
{
Waiter waiter;
return waiter.wait_until(*this, lock, move(should_wake));
}
template<typename T, LockRank Rank, CallableAs<bool, T&> F>
ErrorOr<void> wait_until(SpinlockProtected<T, Rank>& spinlock_protected, F should_wake)
InterruptedOr<void> wait_until(SpinlockProtected<T, Rank>& spinlock_protected, F should_wake)
{
Waiter waiter;
return waiter.wait_until(*this, spinlock_protected, move(should_wake));