.. _select:

tmc::select()
-----------------------------------------------------------------------------------

``select()`` awaits several awaitables and returns the result of the first one to complete, in a ``std::variant``. As soon as the winner completes, each other awaitable (the losers) is cancelled, and their results are discarded. The variant's ``index()`` tells you which awaitable won; its value holds that awaitable's result (a ``void`` result becomes ``std::monostate``).

Each awaitable must be paired with a way to cancel it, using :cpp:class:`tmc::cancellable`. When the winner is decided, ``select()`` runs each loser's canceller exactly once. A canceller may be synchronous or may itself return an awaitable (an async cancel), which ``select()`` awaits.

Contrast this with :literal_ref:`tmc::mux_many<mux_many>` / :literal_ref:`tmc::mux_tuple<mux_tuple>`, which deliver every result as it becomes ready and leave the other awaitables running. ``select()`` instead keeps only the first result and cancels the rest.

Up to 63 awaitables (31 on 32-bit) may be selected over.

API surface:

* :cpp:func:`tmc::select()<tmc::select>` - await the awaitables and return the first result as a ``std::variant``
* :cpp:class:`tmc::cancellable` - pair an awaitable with its cancellation action. Three forms are deduced automatically via CTAD:

  * ``tmc::cancellable(awaitable, functor)`` - a nullary functor that cancels when invoked (sync, or async if it returns an awaitable)
  * ``tmc::cancellable(awaitable, object)`` - an object exposing a ``.cancel()`` method (sync or async)
  * ``tmc::cancellable(object)`` - an object that is itself both the awaitable and exposes a ``.cancel()`` method (sync or async)

Usage Rules
-----------------------------------------------------------------------------------

* Every awaitable passed to ``select()`` must be wrapped in :cpp:class:`tmc::cancellable`.
* If several awaitables complete simultaneously, the winner is the one with the lowest index (leftmost argument); all others are cancelled.
* A cancellation action must be safe to run even after its operation has already completed (it can race with completion), and should not throw.
* ``select()`` waits for the cancelled losers to finish before returning, because they borrow storage from the calling coroutine frame. A canceller that cannot truly stop its operation will delay the return of ``select()`` until that operation completes on its own.
* Do not pass an awaitable as the second argument to :cpp:class:`tmc::cancellable`; pass a nullary functor that *returns* the awaitable, e.g. ``[&]{ return op.async_cancel(); }``. It is invoked lazily, only when the cancel is actually needed.

Usage Examples
-----------------------------------------------------------------------------------

.. tab:: Operation with timeout

   Race an operation against a timeout timer. Whichever fires first wins, and ``select()`` cancels the loser automatically. Here both cancellable forms are shown: a lambda canceller for the operation, and a ``.cancel()``-bearing object for the timer.

   .. code-block:: cpp

      #include "tmc/asio/aw_asio.hpp"
      #include "tmc/asio/ex_asio.hpp"
      #include "tmc/select.hpp"
      #include "tmc/task.hpp"

      #include <asio/error_code.hpp>
      #include <asio/steady_timer.hpp>
      #include <chrono>
      #include <variant>

      // The operation is simulated here with a timer; in practice it would be a
      // network read, a CPU-bound job with a cancellation flag, etc.
      tmc::task<bool> run_with_timeout(std::chrono::milliseconds timeout) {
        asio::steady_timer operation{tmc::asio_executor(), std::chrono::seconds(1)};
        asio::steady_timer deadline{tmc::asio_executor(), timeout};

        // Slots correspond to argument order: index 0 is the operation,
        // index 1 is the timeout.
        std::variant<std::tuple<asio::error_code>, std::tuple<asio::error_code>> result =
          co_await tmc::select(
            // Form 1: a lambda that cancels the operation when invoked.
            tmc::cancellable(
              operation.async_wait(tmc::aw_asio), [&]() { operation.cancel(); }
            ),
            // Form 2: an object with a .cancel() method.
            tmc::cancellable(deadline.async_wait(tmc::aw_asio), deadline)
          );

        // true if the operation finished first; false if it timed out.
        co_return result.index() == 0;
      }

.. tab:: Cancellation forms

   Each awaitable is paired with a canceller via :cpp:class:`tmc::cancellable`. The correct form is deduced from the arguments.

   .. code-block:: cpp

      #include "tmc/select.hpp"

      // Form 1: a nullary functor that cancels when invoked. It may run the cancel
      // synchronously (returns void)...
      tmc::cancellable(timer.async_wait(tmc::aw_asio), [&]{ timer.cancel(); });
      // ...or lazily return an awaitable, which select() awaits (an async cancel).
      tmc::cancellable(op.async_do(), [&]{ return op.async_cancel(); });

      // Form 2: an object exposing a .cancel() method (sync or async). Asio I/O
      // objects fit this form directly.
      tmc::cancellable(timer.async_wait(tmc::aw_asio), timer);
      tmc::cancellable(sock.async_read_some(buf, tmc::aw_asio), sock);

      // Form 3: an object that is itself both the awaitable AND the cancel handle -
      // it can be co_awaited and also exposes .cancel().
      tmc::cancellable(op); // co_await op / op.cancel()

API Reference
-----------------------------------------------------------------------------------
.. doxygenfunction:: tmc::select

.. doxygenclass:: tmc::cancellable
  :members:
