.. _channel:

channel
-----------------------------------------------------------------------------------
Async MPMC unbounded queue using purely zero-copy operation. It is linearizable / FIFO. It can be configured by overriding the Config template parameter.


The channel is accessed through a ``coro_util::chan_tok`` access token, which is
created by :cpp:func:`make_channel()<coro_util::impl::make_channel>`. Tokens share ownership of the channel by
reference counting; to access the channel from multiple threads or tasks concurrently, give each one its
own copy of the token.

Producer (unrestricted) operations:

* :cpp:func:`post()<coro_util::impl::chan_tok::post>`
* :cpp:func:`post_bulk()<coro_util::impl::chan_tok::post_bulk>`
* :cpp:func:`close()<coro_util::impl::chan_tok::close>`

Consumer (unrestricted) operations:

* ``co_await`` :cpp:func:`pull()<coro_util::impl::chan_tok::pull>`
* :cpp:func:`try_pull()<coro_util::impl::chan_tok::try_pull>`

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

.. tab:: Using pull()

   :cpp:func:`pull()<coro_util::impl::chan_tok::pull>` suspends until data is available.

   .. code-block:: cpp

      #include "coro_util/tmc/channel.hpp"
      #include "tmc/task.hpp"
      #include "tmc/spawn_tuple.hpp"

      tmc::task<void> producer(coro_util::chan_tok<int> chan) {
        for (int i = 0; i < 100; ++i) {
          chan.post(i);
        }
        chan.close();
      }

      tmc::task<void> consumer(coro_util::chan_tok<int> chan) {
        // Loop automatically breaks once the channel drains after close()
        while (auto data = co_await chan.pull()) {
          int& v = data.value();
          // do something with v
        }
      }

      tmc::task<void> chan_quickstart() {
        coro_util::chan_tok<int> chan = coro_util::make_channel<int>();
        co_await tmc::spawn_tuple(producer(chan), consumer(chan));
      }

.. tab:: Using try_pull()

   :cpp:func:`try_pull()<coro_util::impl::chan_tok::try_pull>` is non-suspending and must be
   polled. It returns a scoped zero-copy handle whose ``status()`` (or
   ``operator bool()``) indicates the result.

   .. code-block:: cpp

      // Drains all data currently in the channel. Returns true if the channel was empty afterward,
      // and false if the channel was closed afterward.
      tmc::task<bool> consume_all_data(coro_util::chan_tok<int> chan) {
        while (true) {
          auto data = chan.try_pull();
          switch (data.status()) {
            case coro_util::qu_err::OK: {
              int& v = data.value();
              // do something with v
              break;
            }
            case coro_util::qu_err::EMPTY:
              // No data available right now. Try again later.
              co_return true;
            case coro_util::qu_err::CLOSED:
              // The channel has been closed and drained. Do not try again later.
              co_return false;
            default:
              std::unreachable();
          }
        }
      }


API Reference
-----------------------------------------------------------------------------------
.. doxygenfunction:: coro_util::impl::make_channel

.. doxygenclass:: coro_util::impl::chan_tok
  :members:

.. doxygenstruct:: coro_util::chan_default_config
  :members:
