/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <exception>

namespace folly {

/// IMPORTANT: `folly`-internal, do NOT use this in new user code. Instead:
///
///   - `co_yield coro::co_cancelled` to signal that a coro was cancelled.
///
///   - To check for cancellation in `folly::coro` coroutines, use one of:
///       // (1) default behavior
///       co_await coro::co_safe_point
///       // (2) custom behavior
///       auto& ctok = co_await coro::co_current_cancellation_token;
///       if (ctok.isCancellationRequested()) {
///         /* ... do stuff ... */
///         co_yield coro::co_cancelled;
///       }
///
///   - Store `stopped_result` to obtain a `result<T>` or `non_value_result`
///     in a stopped state. To check for it, use `res.has_stopped()`.
///
///   - To avoid depending on `OperationCancelled` in code that would do
///     `try-catch` in `folly::coro` coroutines, do this instead:
///
///       auto res = co_await coro::value_or_error_or_stopped(
///           mightGetCancelled());
///       if (auto ex = get_exception<MyErr>(res)) {
///         // Handle error here; `ex` quacks like `const MyErr*`.
///       } else if (res.has_stopped()) {
///         // Handle cancellation here
///         co_yield coro::co_cancelled;
///       } else { // get value, or propagate unhandled errors/cancellation
///         auto v = co_await folly::or_unwind(std::move(res));
///       }
///
///     OR: If you want to transparently propagate cancellation to the parent
///     coro, use `value_or_error()` instead.  Then, a "cancellation" /
///     "stopped" completion will promptly end the current coro WITHOUT
///     throwing, and its parent will see a "stopped" completion as well.  As
///     with `co_nothrow`, ask for help in Coroutines@ if your current scope
///     requires async cleanup / async RAII (such as "async scope" usage or
///     other background work).
///
/// Rationale / purpose: For now, `folly` uses the `OperationCancelled`
/// exception to signal "this work was stopped".  However, as of C++26 (see
/// https://wg21.link/P2300, per the arguments in https://wg21.link/P1677),
/// standard C++ differentiates between "value", "error", and "stopped"
/// completions.  Therefore, we ask end-user code to use the above
/// cancellation-specific constructs, WITHOUT assuming that cancellation /
/// stopping is implemented as an exception.
struct OperationCancelled final : public std::exception {
  const char* what() const noexcept override {
    return "coroutine operation cancelled";
  }
};

} // namespace folly
