//! Sync errors #pragma once #include #include namespace fx::error { /// Thrown when an operation is aborted via a stop signal. /// /// The state may be inconsistent after the thrown exception. struct OperationCancelledError : virtual std::runtime_error { OperationCancelledError() noexcept; OperationCancelledError(OperationCancelledError const&) noexcept = default; OperationCancelledError& operator=(OperationCancelledError const&) noexcept = default; virtual ~OperationCancelledError(); }; /// Thrown when a task is cancelled via a stop signal. /// /// The state is **not** left in an inconsistent state after the thrown exception. struct TaskCancelledError final : OperationCancelledError { TaskCancelledError() noexcept; TaskCancelledError(TaskCancelledError const&) noexcept = default; TaskCancelledError& operator=(TaskCancelledError const&) noexcept = default; virtual ~TaskCancelledError(); }; // Throw an `OperationCancelledError` (or, optionally, a `TaskCancelledError` if there is a completed task that was cancelled instead of an operation being aborted.) [[gnu::noinline, gnu::cold, noreturn]] void throw_cancelled(bool clean = false); [[gnu::gnu_inline]] extern inline void throw_if_cancelled(std::stop_token const& st, bool clean = false) { if(not st.stop_requested()) return; else throw_cancelled(clean); } namespace details [[gnu::visibility("internal")]] { // NOTE: This assumes the existence of `extern "C" void ::throw_last_system_error()` which throws the current `errno` as a generic-categoried `std::system_error`. If that does not exist, it is re-implemented below. (XXX: There is no reason for this now? We have an impl to call... Ffs...) // static void throw_last_system_error() __attribute__((cold, noreturn, weakref("throw_last_system_error"))); } [[gnu::gnu_inline, gnu::cold, noreturn]] extern inline void throw_last_system_error() { if(__builtin_expect(details::throw_last_system_error != nullptr, true)) details::throw_last_system_error(); else (&throw_last_system_error)(); __builtin_unreachable(); } }