.. _fork_group:

tmc::fork_group()
-----------------------------------------------------------------------------------
``fork_group()`` provides an imperative interface for forking multiple awaitables. Unlike ``spawn_many()`` or ``spawn_tuple()``, which require all awaitables to be known upfront, ``fork_group()`` allows you to incrementally fork awaitables and join them all at a later time.

Each awaitable is initiated immediately (concurrently) when ``fork()`` is called. The ``fork_group`` must be awaited to join all forked tasks before it goes out of scope.

Template Parameters
------------------------------------------------------------------------------------------------
``fork_group()`` has two template parameters that determine its storage strategy:

* ``MaxCount`` (default: 0): The maximum number of awaitables that will be forked.
* ``Result`` (default: void): The result type of the awaitables that will be forked.

**Storage Strategies:**

* If ``Result`` is ``void``, then ``MaxCount`` must be 0. No storage is needed, so an unlimited number of void-returning awaitables can be forked with no space overhead.
* If ``Result`` is non-void and ``MaxCount`` is non-zero, a fixed-size ``std::array<Result, MaxCount>`` is used. If fewer than ``MaxCount`` awaitables are forked, the remaining results are default-initialized.
* If ``Result`` is non-void and ``MaxCount`` is 0, you must pass a ``RuntimeMaxCount`` parameter at construction. A ``std::vector<Result>`` of this size is pre-allocated.

If ``Result`` is not default-constructible, each value will instead be wrapped into a ``std::optional<Result>``.

Imperative Fork Interface
------------------------------------------------------------------------------------------------

``.fork()``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Initiates an awaitable immediately on the specified executor and priority. The awaitable can be any awaitable type as long as its result type matches the ``fork_group``'s ``Result`` type.

.. code-block:: cpp

   auto fg = tmc::fork_group();
   fg.fork(task_void());
   fg.fork(some_awaitable());
   co_await std::move(fg);

``.fork_clang()``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Similar to ``fork()`` but allows the forked task's allocation to be elided via HALO (Heap Allocation eLision Optimization) when compiled with Clang 20+. See :ref:`HALO<halo>` for details.

**IMPORTANT:** You must ``co_await`` the result of ``fork_clang()`` immediately for HALO to be possible.

.. code-block:: cpp

   auto fg = tmc::fork_group();
   co_await fg.fork_clang(task_void());
   co_await fg.fork_clang(another_task());
   co_await std::move(fg);

**WARNING:** Do not use ``fork_clang()`` in a loop, as Clang will try to reuse the same allocation for multiple active coroutines, causing crashes.

.. code-block:: cpp

   // WRONG - Will crash!
   auto fg = tmc::fork_group();
   for (int i = 0; i < 2; i++) {
       co_await fg.fork_clang(task(i));
   }
   co_await std::move(fg);

   // The equivalent without the loop works fine.
   auto fg = tmc::fork_group();
   co_await fg.fork_clang(task(0));
   co_await fg.fork_clang(task(1));
   co_await std::move(fg);

   // Or you can use regular fork() in a loop
   // (but HALO will not be performed)
   auto fg = tmc::fork_group();
   for (int i = 0; i < 2; i++) {
       fg.fork(task(i));
   }
   co_await std::move(fg);

Awaitable Customizations
------------------------------------------------------------------------------------------------
The ``fork_group`` awaitable supports this :ref:`Awaitable Customization <awaitable_customizations>`:
:literal_ref:`resume_on()<resume_on>`

Note that ``fork_group`` does not support ``run_on()`` or ``with_priority()`` on the group itself. Instead, you specify the executor and priority for each individual forked task via the ``fork()`` method parameters.

API Reference
------------------------------------------------------------------------------------------------

.. doxygenfunction:: tmc::fork_group()

.. doxygenfunction:: tmc::fork_group(Awaitable&& Aw)

.. doxygenfunction:: tmc::fork_group(size_t RuntimeMaxCount)

.. doxygenfunction:: tmc::fork_group(size_t RuntimeMaxCount, Awaitable&& Aw)

.. doxygenclass:: tmc::aw_fork_group
   :members:
