{"id":13419246,"url":"https://github.com/lewissbaker/cppcoro","last_synced_at":"2025-05-14T19:04:34.064Z","repository":{"id":40523815,"uuid":"85565658","full_name":"lewissbaker/cppcoro","owner":"lewissbaker","description":"A library of C++ coroutine abstractions for the coroutines TS","archived":false,"fork":false,"pushed_at":"2024-01-09T06:59:48.000Z","size":852,"stargazers_count":3550,"open_issues_count":110,"forks_count":480,"subscribers_count":125,"default_branch":"master","last_synced_at":"2025-04-06T08:09:33.414Z","etag":null,"topics":["async","async-await","asynchronous-programming","asyncio","clang","coroutines","coroutines-ts","cplusplus","cpp","linux","msvc","windows"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lewissbaker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-20T10:48:10.000Z","updated_at":"2025-04-06T04:48:26.000Z","dependencies_parsed_at":"2024-09-30T19:40:28.542Z","dependency_job_id":"b0c695a6-c38a-4429-afcf-9e87f83286b6","html_url":"https://github.com/lewissbaker/cppcoro","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewissbaker%2Fcppcoro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewissbaker%2Fcppcoro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewissbaker%2Fcppcoro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewissbaker%2Fcppcoro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lewissbaker","download_url":"https://codeload.github.com/lewissbaker/cppcoro/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248710404,"owners_count":21149185,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","async-await","asynchronous-programming","asyncio","clang","coroutines","coroutines-ts","cplusplus","cpp","linux","msvc","windows"],"created_at":"2024-07-30T22:01:13.321Z","updated_at":"2025-04-13T11:45:38.616Z","avatar_url":"https://github.com/lewissbaker.png","language":"C++","readme":"# CppCoro - A coroutine library for C++\n\nThe 'cppcoro' library provides a large set of general-purpose primitives for making use of the coroutines TS proposal described in [N4680](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4680.pdf).\n\nThese include:\n* Coroutine Types\n  * [`task\u003cT\u003e`](#taskt)\n  * [`shared_task\u003cT\u003e`](#shared_taskt)\n  * [`generator\u003cT\u003e`](#generatort)\n  * [`recursive_generator\u003cT\u003e`](#recursive_generatort)\n  * [`async_generator\u003cT\u003e`](#async_generatort)\n* Awaitable Types\n  * [`single_consumer_event`](#single_consumer_event)\n  * [`single_consumer_async_auto_reset_event`](#single_consumer_async_auto_reset_event)\n  * [`async_mutex`](#async_mutex)\n  * [`async_manual_reset_event`](#async_manual_reset_event)\n  * [`async_auto_reset_event`](#async_auto_reset_event)\n  * [`async_latch`](#async_latch)\n  * [`sequence_barrier`](#sequence_barrier)\n  * [`multi_producer_sequencer`](#multi_producer_sequencer)\n  * [`single_producer_sequencer`](#single_producer_sequencer)\n* Functions\n  * [`sync_wait()`](#sync_wait)\n  * [`when_all()`](#when_all)\n  * [`when_all_ready()`](#when_all_ready)\n  * [`fmap()`](#fmap)\n  * [`schedule_on()`](#schedule_on)\n  * [`resume_on()`](#resume_on)\n* [Cancellation](#Cancellation)\n  * `cancellation_token`\n  * `cancellation_source`\n  * `cancellation_registration`\n* Schedulers and I/O\n  * [`static_thread_pool`](#static_thread_pool)\n  * [`io_service` and `io_work_scope`](#io_service-and-io_work_scope)\n  * [`file`, `readable_file`, `writable_file`](#file-readable_file-writable_file)\n  * [`read_only_file`, `write_only_file`, `read_write_file`](#read_only_file-write_only_file-read_write_file)\n* Networking\n  * [`socket`](#socket)\n  * [`ip_address`, `ipv4_address`, `ipv6_address`](#ip_address-ipv4_address-ipv6_address)\n  * [`ip_endpoint`, `ipv4_endpoint`, `ipv6_endpoint`](#ip_endpoint-ipv4_endpoint-ipv6_endpoint)\n* Metafunctions\n  * [`is_awaitable\u003cT\u003e`](#is_awaitablet)\n  * [`awaitable_traits\u003cT\u003e`](#awaitable_traitst)\n* Concepts\n  * [`Awaitable\u003cT\u003e`](#Awaitablet-concept)\n  * [`Awaiter\u003cT\u003e`](#Awaitert-concept)\n  * [`Scheduler`](#Scheduler-concept)\n  * [`DelayedScheduler`](#DelayedScheduler-concept)\n\nThis library is an experimental library that is exploring the space of high-performance,\nscalable asynchronous programming abstractions that can be built on top of the C++ coroutines\nproposal.\n\nIt has been open-sourced in the hope that others will find it useful and that the C++ community\ncan provide feedback on it and ways to improve it.\n\nIt requires a compiler that supports the coroutines TS:\n- Windows + Visual Studio 2017 [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/lewissbaker/cppcoro?branch=master\u0026svg=true\u0026passingText=master%20-%20OK\u0026failingText=master%20-%20Failing\u0026pendingText=master%20-%20Pending)](https://ci.appveyor.com/project/lewissbaker/cppcoro/branch/master)\n- Linux + Clang 5.0/6.0 + libc++ [![Build Status](https://travis-ci.org/lewissbaker/cppcoro.svg?branch=master)](https://travis-ci.org/lewissbaker/cppcoro)\n\nThe Linux version is functional except for the `io_context` and file I/O related classes which have not yet been implemented for Linux (see issue [#15](https://github.com/lewissbaker/cppcoro/issues/15) for more info).\n\n# Class Details\n\n## `task\u003cT\u003e`\n\nA task represents an asynchronous computation that is executed lazily in\nthat the execution of the coroutine does not start until the task is awaited.\n\nExample:\n```c++\n#include \u003ccppcoro/read_only_file.hpp\u003e\n#include \u003ccppcoro/task.hpp\u003e\n\ncppcoro::task\u003cint\u003e count_lines(std::string path)\n{\n  auto file = co_await cppcoro::read_only_file::open(path);\n\n  int lineCount = 0;\n\n  char buffer[1024];\n  size_t bytesRead;\n  std::uint64_t offset = 0;\n  do\n  {\n    bytesRead = co_await file.read(offset, buffer, sizeof(buffer));\n    lineCount += std::count(buffer, buffer + bytesRead, '\\n');\n    offset += bytesRead;\n  } while (bytesRead \u003e 0);\n\n  co_return lineCount;\n}\n\ncppcoro::task\u003c\u003e usage_example()\n{\n  // Calling function creates a new task but doesn't start\n  // executing the coroutine yet.\n  cppcoro::task\u003cint\u003e countTask = count_lines(\"foo.txt\");\n\n  // ...\n\n  // Coroutine is only started when we later co_await the task.\n  int lineCount = co_await countTask;\n\n  std::cout \u003c\u003c \"line count = \" \u003c\u003c lineCount \u003c\u003c std::endl;\n}\n```\n\nAPI Overview:\n```c++\n// \u003ccppcoro/task.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename T\u003e\n  class task\n  {\n  public:\n\n    using promise_type = \u003cunspecified\u003e;\n    using value_type = T;\n\n    task() noexcept;\n\n    task(task\u0026\u0026 other) noexcept;\n    task\u0026 operator=(task\u0026\u0026 other);\n\n    // task is a move-only type.\n    task(const task\u0026 other) = delete;\n    task\u0026 operator=(const task\u0026 other) = delete;\n\n    // Query if the task result is ready.\n    bool is_ready() const noexcept;\n\n    // Wait for the task to complete and return the result or rethrow the\n    // exception if the operation completed with an unhandled exception.\n    //\n    // If the task is not yet ready then the awaiting coroutine will be\n    // suspended until the task completes. If the the task is_ready() then\n    // this operation will return the result synchronously without suspending.\n    Awaiter\u003cT\u0026\u003e operator co_await() const \u0026 noexcept;\n    Awaiter\u003cT\u0026\u0026\u003e operator co_await() const \u0026\u0026 noexcept;\n\n    // Returns an awaitable that can be co_await'ed to suspend the current\n    // coroutine until the task completes.\n    //\n    // The 'co_await t.when_ready()' expression differs from 'co_await t' in\n    // that when_ready() only performs synchronization, it does not return\n    // the result or rethrow the exception.\n    //\n    // This can be useful if you want to synchronize with the task without\n    // the possibility of it throwing an exception.\n    Awaitable\u003cvoid\u003e when_ready() const noexcept;\n  };\n\n  template\u003ctypename T\u003e\n  void swap(task\u003cT\u003e\u0026 a, task\u003cT\u003e\u0026 b);\n\n  // Creates a task that yields the result of co_await'ing the specified awaitable.\n  //\n  // This can be used as a form of type-erasure of the concrete awaitable, allowing\n  // different awaitables that return the same await-result type to be stored in\n  // the same task\u003cRESULT\u003e type.\n  template\u003c\n    typename AWAITABLE,\n    typename RESULT = typename awaitable_traits\u003cAWAITABLE\u003e::await_result_t\u003e\n  task\u003cRESULT\u003e make_task(AWAITABLE awaitable);\n}\n```\n\nYou can create a `task\u003cT\u003e` object by calling a coroutine function that returns\na `task\u003cT\u003e`.\n\nThe coroutine must contain a usage of either `co_await` or `co_return`.\nNote that a `task\u003cT\u003e` coroutine may not use the `co_yield` keyword.\n\nWhen a coroutine that returns a `task\u003cT\u003e` is called, a coroutine frame\nis allocated if necessary and the parameters are captured in the coroutine\nframe. The coroutine is suspended at the start of the coroutine body and\nexecution is returned to the caller and a `task\u003cT\u003e` value that represents\nthe asynchronous computation is returned from the function call.\n\nThe coroutine body will start executing when the `task\u003cT\u003e` value is\n`co_await`ed. This will suspend the awaiting coroutine and start execution\nof the coroutine associated with the awaited `task\u003cT\u003e` value.\n\nThe awaiting coroutine will later be resumed on the thread that completes\nexecution of the awaited `task\u003cT\u003e`'s coroutine. ie. the thread that\nexecutes the `co_return` or that throws an unhandled exception that terminates\nexecution of the coroutine.\n\nIf the task has already run to completion then awaiting it again will obtain\nthe already-computed result without suspending the awaiting coroutine.\n\nIf the `task` object is destroyed before it is awaited then the coroutine\nnever executes and the destructor simply destructs the captured parameters\nand frees any memory used by the coroutine frame.\n\n## `shared_task\u003cT\u003e`\n\nThe `shared_task\u003cT\u003e` class is a coroutine type that yields a single value\nasynchronously.\n\nIt is 'lazy' in that execution of the task does not start until it is awaited by some\ncoroutine.\n\nIt is 'shared' in that the task value can be copied, allowing multiple references to\nthe result of the task to be created. It also allows multiple coroutines to\nconcurrently await the result.\n\nThe task will start executing on the thread that first `co_await`s the task.\nSubsequent awaiters will either be suspended and be queued for resumption\nwhen the task completes or will continue synchronously if the task has\nalready run to completion.\n\nIf an awaiter is suspended while waiting for the task to complete then\nit will be resumed on the thread that completes execution of the task.\nie. the thread that executes the `co_return` or that throws the unhandled\nexception that terminates execution of the coroutine.\n\nAPI Summary\n```c++\nnamespace cppcoro\n{\n  template\u003ctypename T = void\u003e\n  class shared_task\n  {\n  public:\n\n    using promise_type = \u003cunspecified\u003e;\n    using value_type = T;\n\n    shared_task() noexcept;\n    shared_task(const shared_task\u0026 other) noexcept;\n    shared_task(shared_task\u0026\u0026 other) noexcept;\n    shared_task\u0026 operator=(const shared_task\u0026 other) noexcept;\n    shared_task\u0026 operator=(shared_task\u0026\u0026 other) noexcept;\n\n    void swap(shared_task\u0026 other) noexcept;\n\n    // Query if the task has completed and the result is ready.\n    bool is_ready() const noexcept;\n\n    // Returns an operation that when awaited will suspend the\n    // current coroutine until the task completes and the result\n    // is available.\n    //\n    // The type of the result of the 'co_await someTask' expression\n    // is an l-value reference to the task's result value (unless T\n    // is void in which case the expression has type 'void').\n    // If the task completed with an unhandled exception then the\n    // exception will be rethrown by the co_await expression.\n    Awaiter\u003cT\u0026\u003e operator co_await() const noexcept;\n\n    // Returns an operation that when awaited will suspend the\n    // calling coroutine until the task completes and the result\n    // is available.\n    //\n    // The result is not returned from the co_await expression.\n    // This can be used to synchronize with the task without the\n    // possibility of the co_await expression throwing an exception.\n    Awaiter\u003cvoid\u003e when_ready() const noexcept;\n\n  };\n\n  template\u003ctypename T\u003e\n  bool operator==(const shared_task\u003cT\u003e\u0026 a, const shared_task\u003cT\u003e\u0026 b) noexcept;\n  template\u003ctypename T\u003e\n  bool operator!=(const shared_task\u003cT\u003e\u0026 a, const shared_task\u003cT\u003e\u0026 b) noexcept;\n\n  template\u003ctypename T\u003e\n  void swap(shared_task\u003cT\u003e\u0026 a, shared_task\u003cT\u003e\u0026 b) noexcept;\n\n  // Wrap an awaitable value in a shared_task to allow multiple coroutines\n  // to concurrently await the result.\n  template\u003c\n    typename AWAITABLE,\n    typename RESULT = typename awaitable_traits\u003cAWAITABLE\u003e::await_result_t\u003e\n  shared_task\u003cRESULT\u003e make_shared_task(AWAITABLE awaitable);\n}\n```\n\nAll const-methods on `shared_task\u003cT\u003e` are safe to call concurrently with other\nconst-methods on the same instance from multiple threads. It is not safe to call\nnon-const methods of `shared_task\u003cT\u003e` concurrently with any other method on the\nsame instance of a `shared_task\u003cT\u003e`.\n\n### Comparison to `task\u003cT\u003e`\n\nThe `shared_task\u003cT\u003e` class is similar to `task\u003cT\u003e` in that the task does\nnot start execution immediately upon the coroutine function being called.\nThe task only starts executing when it is first awaited.\n\nIt differs from `task\u003cT\u003e` in that the resulting task object can be copied,\nallowing multiple task objects to reference the same asynchronous result.\nIt also supports multiple coroutines concurrently awaiting the result of the task.\n\nThe trade-off is that the result is always an l-value reference to the\nresult, never an r-value reference (since the result may be shared) which\nmay limit ability to move-construct the result into a local variable.\nIt also has a slightly higher run-time cost due to the need to maintain\na reference count and support multiple awaiters.\n\n## `generator\u003cT\u003e`\n\nA `generator` represents a coroutine type that produces a sequence of values of type, `T`,\nwhere values are produced lazily and synchronously.\n\nThe coroutine body is able to yield values of type `T` using the `co_yield` keyword.\nNote, however, that the coroutine body is not able to use the `co_await` keyword;\nvalues must be produced synchronously.\n\nFor example:\n```c++\ncppcoro::generator\u003cconst std::uint64_t\u003e fibonacci()\n{\n  std::uint64_t a = 0, b = 1;\n  while (true)\n  {\n    co_yield b;\n    auto tmp = a;\n    a = b;\n    b += tmp;\n  }\n}\n\nvoid usage()\n{\n  for (auto i : fibonacci())\n  {\n    if (i \u003e 1'000'000) break;\n    std::cout \u003c\u003c i \u003c\u003c std::endl;\n  }\n}\n```\n\nWhen a coroutine function returning a `generator\u003cT\u003e` is called the coroutine is created initially suspended.\nExecution of the coroutine enters the coroutine body when the `generator\u003cT\u003e::begin()` method is called and continues until\neither the first `co_yield` statement is reached or the coroutine runs to completion.\n\nIf the returned iterator is not equal to the `end()` iterator then dereferencing the iterator will\nreturn a reference to the value passed to the `co_yield` statement.\n\nCalling `operator++()` on the iterator will resume execution of the coroutine and continue until\neither the next `co_yield` point is reached or the coroutine runs to completion().\n\nAny unhandled exceptions thrown by the coroutine will propagate out of the `begin()` or\n`operator++()` calls to the caller.\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n    template\u003ctypename T\u003e\n    class generator\n    {\n    public:\n\n        using promise_type = \u003cunspecified\u003e;\n\n        class iterator\n        {\n        public:\n            using iterator_category = std::input_iterator_tag;\n            using value_type = std::remove_reference_t\u003cT\u003e;\n            using reference = value_type\u0026;\n            using pointer = value_type*;\n            using difference_type = std::size_t;\n\n            iterator(const iterator\u0026 other) noexcept;\n            iterator\u0026 operator=(const iterator\u0026 other) noexcept;\n\n            // If the generator coroutine throws an unhandled exception before producing\n            // the next element then the exception will propagate out of this call.\n            iterator\u0026 operator++();\n\n            reference operator*() const noexcept;\n            pointer operator-\u003e() const noexcept;\n\n            bool operator==(const iterator\u0026 other) const noexcept;\n            bool operator!=(const iterator\u0026 other) const noexcept;\n        };\n\n        // Constructs to the empty sequence.\n        generator() noexcept;\n\n        generator(generator\u0026\u0026 other) noexcept;\n        generator\u0026 operator=(generator\u0026\u0026 other) noexcept;\n\n        generator(const generator\u0026 other) = delete;\n        generator\u0026 operator=(const generator\u0026) = delete;\n\n        ~generator();\n\n        // Starts executing the generator coroutine which runs until either a value is yielded\n        // or the coroutine runs to completion or an unhandled exception propagates out of the\n        // the coroutine.\n        iterator begin();\n\n        iterator end() noexcept;\n\n        // Swap the contents of two generators.\n        void swap(generator\u0026 other) noexcept;\n\n    };\n\n    template\u003ctypename T\u003e\n    void swap(generator\u003cT\u003e\u0026 a, generator\u003cT\u003e\u0026 b) noexcept;\n\n    // Apply function, func, lazily to each element of the source generator\n    // and yield a sequence of the results of calls to func().\n    template\u003ctypename FUNC, typename T\u003e\n    generator\u003cstd::invoke_result_t\u003cFUNC, T\u0026\u003e\u003e fmap(FUNC func, generator\u003cT\u003e source);\n}\n```\n\n## `recursive_generator\u003cT\u003e`\n\nA `recursive_generator` is similar to a `generator` except that it is designed to more efficiently\nsupport yielding the elements of a nested sequence as elements of an outer sequence.\n\nIn addition to being able to `co_yield` a value of type `T` you can also `co_yield` a value of type `recursive_generator\u003cT\u003e`.\n\nWhen you `co_yield` a `recursive_generator\u003cT\u003e` value the all elements of the yielded generator are yielded as elements of the current generator.\nThe current coroutine is suspended until the consumer has finished consuming all elements of the nested generator, after which point execution\nof the current coroutine will resume execution to produce the next element.\n\nThe benefit of `recursive_generator\u003cT\u003e` over `generator\u003cT\u003e` for iterating over recursive data-structures is that the `iterator::operator++()`\nis able to directly resume the leaf-most coroutine to produce the next element, rather than having to resume/suspend O(depth) coroutines for each element.\nThe down-side is that there is additional overhead\n\nFor example:\n```c++\n// Lists the immediate contents of a directory.\ncppcoro::generator\u003cdir_entry\u003e list_directory(std::filesystem::path path);\n\ncppcoro::recursive_generator\u003cdir_entry\u003e list_directory_recursive(std::filesystem::path path)\n{\n  for (auto\u0026 entry : list_directory(path))\n  {\n    co_yield entry;\n    if (entry.is_directory())\n    {\n      co_yield list_directory_recursive(entry.path());\n    }\n  }\n}\n```\n\nNote that applying the `fmap()` operator to a `recursive_generator\u003cT\u003e` will yield a `generator\u003cU\u003e`\ntype rather than a `recursive_generator\u003cU\u003e`. This is because uses of `fmap` are generally not used\nin recursive contexts and we try to avoid the extra overhead incurred by `recursive_generator`.\n\n## `async_generator\u003cT\u003e`\n\nAn `async_generator` represents a coroutine type that produces a sequence of values of type, `T`, where values are produced lazily and values may be produced asynchronously.\n\nThe coroutine body is able to use both `co_await` and `co_yield` expressions.\n\nConsumers of the generator can use a `for co_await` range-based for-loop to consume the values.\n\nExample\n```c++\ncppcoro::async_generator\u003cint\u003e ticker(int count, threadpool\u0026 tp)\n{\n  for (int i = 0; i \u003c count; ++i)\n  {\n    co_await tp.delay(std::chrono::seconds(1));\n    co_yield i;\n  }\n}\n\ncppcoro::task\u003c\u003e consumer(threadpool\u0026 tp)\n{\n  auto sequence = ticker(10, tp);\n  for co_await(std::uint32_t i : sequence)\n  {\n    std::cout \u003c\u003c \"Tick \" \u003c\u003c i \u003c\u003c std::endl;\n  }\n}\n```\n\nAPI Summary\n```c++\n// \u003ccppcoro/async_generator.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename T\u003e\n  class async_generator\n  {\n  public:\n\n    class iterator\n    {\n    public:\n      using iterator_tag = std::forward_iterator_tag;\n      using difference_type = std::size_t;\n      using value_type = std::remove_reference_t\u003cT\u003e;\n      using reference = value_type\u0026;\n      using pointer = value_type*;\n\n      iterator(const iterator\u0026 other) noexcept;\n      iterator\u0026 operator=(const iterator\u0026 other) noexcept;\n\n      // Resumes the generator coroutine if suspended\n      // Returns an operation object that must be awaited to wait\n      // for the increment operation to complete.\n      // If the coroutine runs to completion then the iterator\n      // will subsequently become equal to the end() iterator.\n      // If the coroutine completes with an unhandled exception then\n      // that exception will be rethrown from the co_await expression.\n      Awaitable\u003citerator\u0026\u003e operator++() noexcept;\n\n      // Dereference the iterator.\n      pointer operator-\u003e() const noexcept;\n      reference operator*() const noexcept;\n\n      bool operator==(const iterator\u0026 other) const noexcept;\n      bool operator!=(const iterator\u0026 other) const noexcept;\n    };\n\n    // Construct to the empty sequence.\n    async_generator() noexcept;\n    async_generator(const async_generator\u0026) = delete;\n    async_generator(async_generator\u0026\u0026 other) noexcept;\n    ~async_generator();\n\n    async_generator\u0026 operator=(const async_generator\u0026) = delete;\n    async_generator\u0026 operator=(async_generator\u0026\u0026 other) noexcept;\n\n    void swap(async_generator\u0026 other) noexcept;\n\n    // Starts execution of the coroutine and returns an operation object\n    // that must be awaited to wait for the first value to become available.\n    // The result of co_await'ing the returned object is an iterator that\n    // can be used to advance to subsequent elements of the sequence.\n    //\n    // This method is not valid to be called once the coroutine has\n    // run to completion.\n    Awaitable\u003citerator\u003e begin() noexcept;\n    iterator end() noexcept;\n\n  };\n\n  template\u003ctypename T\u003e\n  void swap(async_generator\u003cT\u003e\u0026 a, async_generator\u003cT\u003e\u0026 b);\n\n  // Apply 'func' to each element of the source generator, yielding a sequence of\n  // the results of calling 'func' on the source elements.\n  template\u003ctypename FUNC, typename T\u003e\n  async_generator\u003cstd::invoke_result_t\u003cFUNC, T\u0026\u003e\u003e fmap(FUNC func, async_generator\u003cT\u003e source);\n}\n```\n\n### Early termination of an async_generator\n\nWhen the `async_generator` object is destructed it requests cancellation of the underlying coroutine.\nIf the coroutine has already run to completion or is currently suspended in a `co_yield` expression\nthen the coroutine is destroyed immediately. Otherwise, the coroutine will continue execution until\nit either runs to completion or reaches the next `co_yield` expression.\n\nWhen the coroutine frame is destroyed the destructors of all variables in scope at that point will be\nexecuted to ensure the resources of the generator are cleaned up.\n\nNote that the caller must ensure that the `async_generator` object must not be destroyed while a\nconsumer coroutine is executing a `co_await` expression waiting for the next item to be produced.\n\n## `single_consumer_event`\n\nThis is a simple manual-reset event type that supports only a single\ncoroutine awaiting it at a time.\nThis can be used to\n\nAPI Summary:\n```c++\n// \u003ccppcoro/single_consumer_event.hpp\u003e\nnamespace cppcoro\n{\n  class single_consumer_event\n  {\n  public:\n    single_consumer_event(bool initiallySet = false) noexcept;\n    bool is_set() const noexcept;\n    void set();\n    void reset() noexcept;\n    Awaiter\u003cvoid\u003e operator co_await() const noexcept;\n  };\n}\n```\n\nExample:\n```c++\n#include \u003ccppcoro/single_consumer_event.hpp\u003e\n\ncppcoro::single_consumer_event event;\nstd::string value;\n\ncppcoro::task\u003c\u003e consumer()\n{\n  // Coroutine will suspend here until some thread calls event.set()\n  // eg. inside the producer() function below.\n  co_await event;\n\n  std::cout \u003c\u003c value \u003c\u003c std::endl;\n}\n\nvoid producer()\n{\n  value = \"foo\";\n\n  // This will resume the consumer() coroutine inside the call to set()\n  // if it is currently suspended.\n  event.set();\n}\n```\n\n## `single_consumer_async_auto_reset_event`\n\nThis class provides an async synchronization primitive that allows a single coroutine to\nwait until the event is signalled by a call to the `set()` method.\n\nOnce the coroutine that is awaiting the event is released by either a prior or subsequent call to `set()`\nthe event is automatically reset back to the 'not set' state.\n\nThis class is a more efficient version of `async_auto_reset_event` that can be used in cases where\nonly a single coroutine will be awaiting the event at a time. If you need to support multiple concurrent\nawaiting coroutines on the event then use the `async_auto_reset_event` class instead.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/single_consumer_async_auto_reset_event.hpp\u003e\nnamespace cppcoro\n{\n  class single_consumer_async_auto_reset_event\n  {\n  public:\n\n    single_consumer_async_auto_reset_event(\n      bool initiallySet = false) noexcept;\n\n    // Change the event to the 'set' state. If a coroutine is awaiting the\n    // event then the event is immediately transitioned back to the 'not set'\n    // state and the coroutine is resumed.\n    void set() noexcept;\n\n    // Returns an Awaitable type that can be awaited to wait until\n    // the event becomes 'set' via a call to the .set() method. If\n    // the event is already in the 'set' state then the coroutine\n    // continues without suspending.\n    // The event is automatically reset back to the 'not set' state\n    // before resuming the coroutine.\n    Awaiter\u003cvoid\u003e operator co_await() const noexcept;\n\n  };\n}\n```\n\nExample Usage:\n```c++\nstd::atomic\u003cint\u003e value;\ncppcoro::single_consumer_async_auto_reset_event valueDecreasedEvent;\n\ncppcoro::task\u003c\u003e wait_until_value_is_below(int limit)\n{\n  while (value.load(std::memory_order_relaxed) \u003e= limit)\n  {\n    // Wait until there has been some change that we're interested in.\n    co_await valueDecreasedEvent;\n  }\n}\n\nvoid change_value(int delta)\n{\n  value.fetch_add(delta, std::memory_order_relaxed);\n  // Notify the waiter if there has been some change.\n  if (delta \u003c 0) valueDecreasedEvent.set();\n}\n```\n\n## `async_mutex`\n\nProvides a simple mutual exclusion abstraction that allows the caller to 'co_await' the mutex\nfrom within a coroutine to suspend the coroutine until the mutex lock is acquired.\n\nThe implementation is lock-free in that a coroutine that awaits the mutex will not\nblock the thread but will instead suspend the coroutine and later resume it inside\nthe call to `unlock()` by the previous lock-holder.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/async_mutex.hpp\u003e\nnamespace cppcoro\n{\n  class async_mutex_lock;\n  class async_mutex_lock_operation;\n  class async_mutex_scoped_lock_operation;\n\n  class async_mutex\n  {\n  public:\n    async_mutex() noexcept;\n    ~async_mutex();\n\n    async_mutex(const async_mutex\u0026) = delete;\n    async_mutex\u0026 operator(const async_mutex\u0026) = delete;\n\n    bool try_lock() noexcept;\n    async_mutex_lock_operation lock_async() noexcept;\n    async_mutex_scoped_lock_operation scoped_lock_async() noexcept;\n    void unlock();\n  };\n\n  class async_mutex_lock_operation\n  {\n  public:\n    bool await_ready() const noexcept;\n    bool await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter) noexcept;\n    void await_resume() const noexcept;\n  };\n\n  class async_mutex_scoped_lock_operation\n  {\n  public:\n    bool await_ready() const noexcept;\n    bool await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter) noexcept;\n    [[nodiscard]] async_mutex_lock await_resume() const noexcept;\n  };\n\n  class async_mutex_lock\n  {\n  public:\n    // Takes ownership of the lock.\n    async_mutex_lock(async_mutex\u0026 mutex, std::adopt_lock_t) noexcept;\n\n    // Transfer ownership of the lock.\n    async_mutex_lock(async_mutex_lock\u0026\u0026 other) noexcept;\n\n    async_mutex_lock(const async_mutex_lock\u0026) = delete;\n    async_mutex_lock\u0026 operator=(const async_mutex_lock\u0026) = delete;\n\n    // Releases the lock by calling unlock() on the mutex.\n    ~async_mutex_lock();\n  };\n}\n```\n\nExample usage:\n```c++\n#include \u003ccppcoro/async_mutex.hpp\u003e\n#include \u003ccppcoro/task.hpp\u003e\n#include \u003cset\u003e\n#include \u003cstring\u003e\n\ncppcoro::async_mutex mutex;\nstd::set\u003cstd::string\u003e values;\n\ncppcoro::task\u003c\u003e add_item(std::string value)\n{\n  cppcoro::async_mutex_lock lock = co_await mutex.scoped_lock_async();\n  values.insert(std::move(value));\n}\n```\n\n## `async_manual_reset_event`\n\nA manual-reset event is a coroutine/thread-synchronization primitive that allows one or more threads\nto wait until the event is signalled by a thread that calls `set()`.\n\nThe event is in one of two states; *'set'* and *'not set'*.\n\nIf the event is in the *'set'* state when a coroutine awaits the event then the coroutine\ncontinues without suspending. However if the coroutine is in the *'not set'* state then the\ncoroutine is suspended until some thread subsequently calls the `set()` method.\n\nAny threads that were suspended while waiting for the event to become *'set'* will be resumed\ninside the next call to `set()` by some thread.\n\nNote that you must ensure that no coroutines are awaiting a *'not set'* event when the\nevent is destructed as they will not be resumed.\n\nExample:\n```c++\ncppcoro::async_manual_reset_event event;\nstd::string value;\n\nvoid producer()\n{\n  value = get_some_string_value();\n\n  // Publish a value by setting the event.\n  event.set();\n}\n\n// Can be called many times to create many tasks.\n// All consumer tasks will wait until value has been published.\ncppcoro::task\u003c\u003e consumer()\n{\n  // Wait until value has been published by awaiting event.\n  co_await event;\n\n  consume_value(value);\n}\n```\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n  class async_manual_reset_event_operation;\n\n  class async_manual_reset_event\n  {\n  public:\n    async_manual_reset_event(bool initiallySet = false) noexcept;\n    ~async_manual_reset_event();\n\n    async_manual_reset_event(const async_manual_reset_event\u0026) = delete;\n    async_manual_reset_event(async_manual_reset_event\u0026\u0026) = delete;\n    async_manual_reset_event\u0026 operator=(const async_manual_reset_event\u0026) = delete;\n    async_manual_reset_event\u0026 operator=(async_manual_reset_event\u0026\u0026) = delete;\n\n    // Wait until the event becomes set.\n    async_manual_reset_event_operation operator co_await() const noexcept;\n\n    bool is_set() const noexcept;\n\n    void set() noexcept;\n\n    void reset() noexcept;\n\n  };\n\n  class async_manual_reset_event_operation\n  {\n  public:\n    async_manual_reset_event_operation(async_manual_reset_event\u0026 event) noexcept;\n\n    bool await_ready() const noexcept;\n    bool await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter) noexcept;\n    void await_resume() const noexcept;\n  };\n}\n```\n\n## `async_auto_reset_event`\n\nAn auto-reset event is a coroutine/thread-synchronization primitive that allows one or more threads\nto wait until the event is signalled by a thread by calling `set()`.\n\nOnce a coroutine that is awaiting the event is released by either a prior or subsequent call to `set()`\nthe event is automatically reset back to the 'not set' state.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/async_auto_reset_event.hpp\u003e\nnamespace cppcoro\n{\n  class async_auto_reset_event_operation;\n\n  class async_auto_reset_event\n  {\n  public:\n\n    async_auto_reset_event(bool initiallySet = false) noexcept;\n\n    ~async_auto_reset_event();\n\n    async_auto_reset_event(const async_auto_reset_event\u0026) = delete;\n    async_auto_reset_event(async_auto_reset_event\u0026\u0026) = delete;\n    async_auto_reset_event\u0026 operator=(const async_auto_reset_event\u0026) = delete;\n    async_auto_reset_event\u0026 operator=(async_auto_reset_event\u0026\u0026) = delete;\n\n    // Wait for the event to enter the 'set' state.\n    //\n    // If the event is already 'set' then the event is set to the 'not set'\n    // state and the awaiting coroutine continues without suspending.\n    // Otherwise, the coroutine is suspended and later resumed when some\n    // thread calls 'set()'.\n    //\n    // Note that the coroutine may be resumed inside a call to 'set()'\n    // or inside another thread's call to 'operator co_await()'.\n    async_auto_reset_event_operation operator co_await() const noexcept;\n\n    // Set the state of the event to 'set'.\n    //\n    // If there are pending coroutines awaiting the event then one\n    // pending coroutine is resumed and the state is immediately\n    // set back to the 'not set' state.\n    //\n    // This operation is a no-op if the event was already 'set'.\n    void set() noexcept;\n\n    // Set the state of the event to 'not-set'.\n    //\n    // This is a no-op if the state was already 'not set'.\n    void reset() noexcept;\n\n  };\n\n  class async_auto_reset_event_operation\n  {\n  public:\n    explicit async_auto_reset_event_operation(async_auto_reset_event\u0026 event) noexcept;\n    async_auto_reset_event_operation(const async_auto_reset_event_operation\u0026 other) noexcept;\n\n    bool await_ready() const noexcept;\n    bool await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter) noexcept;\n    void await_resume() const noexcept;\n\n  };\n}\n```\n\n## `async_latch`\n\nAn async latch is a synchronization primitive that allows coroutines to asynchronously\nwait until a counter has been decremented to zero.\n\nThe latch is a single-use object. Once the counter reaches zero the latch becomes 'ready'\nand will remain ready until the latch is destroyed.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/async_latch.hpp\u003e\nnamespace cppcoro\n{\n  class async_latch\n  {\n  public:\n\n    // Initialise the latch with the specified count.\n    async_latch(std::ptrdiff_t initialCount) noexcept;\n\n    // Query if the count has reached zero yet.\n    bool is_ready() const noexcept;\n\n    // Decrement the count by n.\n    // This will resume any waiting coroutines if the count reaches zero\n    // as a result of this call.\n    // It is undefined behaviour to decrement the count below zero.\n    void count_down(std::ptrdiff_t n = 1) noexcept;\n\n    // Wait until the latch becomes ready.\n    // If the latch count is not yet zero then the awaiting coroutine will\n    // be suspended and later resumed by a call to count_down() that decrements\n    // the count to zero. If the latch count was already zero then the coroutine\n    // continues without suspending.\n    Awaiter\u003cvoid\u003e operator co_await() const noexcept;\n\n  };\n}\n```\n\n## `sequence_barrier`\n\nA `sequence_barrier` is a synchronization primitive that allows a single-producer\nand multiple consumers to coordinate with respect to a monotonically increasing\nsequence number.\n\nA single producer advances the sequence number by publishing new sequence numbers\nin a monotonically increasing order. One or more consumers can query the last\npublished sequence number and can wait until a particular sequence number has been\npublished.\n\nA sequence barrier can be used to represent a cursor into a thread-safe producer/consumer\nring-buffer\n\nSee the LMAX Disruptor pattern for more background:\nhttps://lmax-exchange.github.io/disruptor/files/Disruptor-1.0.pdf\n\nAPI Synopsis:\n```c++\nnamespace cppcoro\n{\n  template\u003ctypename SEQUENCE = std::size_t,\n           typename TRAITS = sequence_traits\u003cSEQUENCE\u003e\u003e\n  class sequence_barrier\n  {\n  public:\n    sequence_barrier(SEQUENCE initialSequence = TRAITS::initial_sequence) noexcept;\n\t~sequence_barrier();\n\n\tSEQUENCE last_published() const noexcept;\n\n\t// Wait until the specified targetSequence number has been published.\n\t//\n\t// If the operation does not complete synchronously then the awaiting\n\t// coroutine is resumed on the specified scheduler. Otherwise, the\n\t// coroutine continues without suspending.\n\t//\n\t// The co_await expression resumes with the updated last_published()\n\t// value, which is guaranteed to be at least 'targetSequence'.\n\ttemplate\u003ctypename SCHEDULER\u003e\n\t[[nodiscard]]\n\tAwaitable\u003cSEQUENCE\u003e wait_until_published(SEQUENCE targetSequence,\n                                             SCHEDULER\u0026 scheduler) const noexcept;\n\n    void publish(SEQUENCE sequence) noexcept;\n  };\n}\n```\n\n## `single_producer_sequencer`\n\nA `single_producer_sequencer` is a synchronization primitive that can be used to\ncoordinate access to a ring-buffer for a single producer and one or more consumers.\n\nA producer first acquires one or more slots in a ring-buffer, writes to the ring-buffer\nelements corresponding to those slots, and then finally publishes the values written to\nthose slots. A producer can never produce more than 'bufferSize' elements in advance\nof where the consumer has consumed up to.\n\nA consumer then waits for certain elements to be published, processes the items and\nthen notifies the producer when it has finished processing items by publishing the\nsequence number it has finished consuming in a `sequence_barrier` object.\n\n\nAPI Synopsis:\n```c++\n// \u003ccppcoro/single_producer_sequencer.hpp\u003e\nnamespace cppcoro\n{\n  template\u003c\n    typename SEQUENCE = std::size_t,\n    typename TRAITS = sequence_traits\u003cSEQUENCE\u003e\u003e\n  class single_producer_sequencer\n  {\n  public:\n    using size_type = typename sequence_range\u003cSEQUENCE, TRAITS\u003e::size_type;\n\n    single_producer_sequencer(\n      const sequence_barrier\u003cSEQUENCE, TRAITS\u003e\u0026 consumerBarrier,\n      std::size_t bufferSize,\n      SEQUENCE initialSequence = TRAITS::initial_sequence) noexcept;\n\n    // Publisher API:\n\n    template\u003ctypename SCHEDULER\u003e\n    [[nodiscard]]\n    Awaitable\u003cSEQUENCE\u003e claim_one(SCHEDULER\u0026 scheduler) noexcept;\n\n    template\u003ctypename SCHEDULER\u003e\n    [[nodiscard]]\n    Awaitable\u003csequence_range\u003cSEQUENCE\u003e\u003e claim_up_to(\n      std::size_t count,\n      SCHEDULER\u0026 scheduler) noexcept;\n\n    void publish(SEQUENCE sequence) noexcept;\n\n    // Consumer API:\n\n    SEQUENCE last_published() const noexcept;\n\n    template\u003ctypename SCHEDULER\u003e\n    [[nodiscard]]\n    Awaitable\u003cSEQUENCE\u003e wait_until_published(\n      SEQUENCE targetSequence,\n      SCHEDULER\u0026 scheduler) const noexcept;\n\n  };\n}\n```\n\nExample usage:\n```c++\nusing namespace cppcoro;\nusing namespace std::chrono;\n\nstruct message\n{\n  int id;\n  steady_clock::time_point timestamp;\n  float data;\n};\n\nconstexpr size_t bufferSize = 16384; // Must be power-of-two\nconstexpr size_t indexMask = bufferSize - 1;\nmessage buffer[bufferSize];\n\ntask\u003cvoid\u003e producer(\n  io_service\u0026 ioSvc,\n  single_producer_sequencer\u003csize_t\u003e\u0026 sequencer)\n{\n  auto start = steady_clock::now();\n  for (int i = 0; i \u003c 1'000'000; ++i)\n  {\n    // Wait until a slot is free in the buffer.\n    size_t seq = co_await sequencer.claim_one(ioSvc);\n\n    // Populate the message.\n    auto\u0026 msg = buffer[seq \u0026 indexMask];\n    msg.id = i;\n    msg.timestamp = steady_clock::now();\n    msg.data = 123;\n\n    // Publish the message.\n    sequencer.publish(seq);\n  }\n\n  // Publish a sentinel\n  auto seq = co_await sequencer.claim_one(ioSvc);\n  auto\u0026 msg = buffer[seq \u0026 indexMask];\n  msg.id = -1;\n  sequencer.publish(seq);\n}\n\ntask\u003cvoid\u003e consumer(\n  static_thread_pool\u0026 threadPool,\n  const single_producer_sequencer\u003csize_t\u003e\u0026 sequencer,\n  sequence_barrier\u003csize_t\u003e\u0026 consumerBarrier)\n{\n  size_t nextToRead = 0;\n  while (true)\n  {\n    // Wait until the next message is available\n    // There may be more than one available.\n    const size_t available = co_await sequencer.wait_until_published(nextToRead, threadPool);\n    do {\n      auto\u0026 msg = buffer[nextToRead \u0026 indexMask];\n      if (msg.id == -1)\n      {\n        consumerBarrier.publish(nextToRead);\n        co_return;\n      }\n\n      processMessage(msg);\n    } while (nextToRead++ != available);\n\n    // Notify the producer that we've finished processing\n    // up to 'nextToRead - 1'.\n    consumerBarrier.publish(available);\n  }\n}\n\ntask\u003cvoid\u003e example(io_service\u0026 ioSvc, static_thread_pool\u0026 threadPool)\n{\n  sequence_barrier\u003csize_t\u003e barrier;\n  single_producer_sequencer\u003csize_t\u003e sequencer{barrier, bufferSize};\n\n  co_await when_all(\n    producer(tp, sequencer),\n    consumer(tp, sequencer, barrier));\n}\n```\n\n## `multi_producer_sequencer`\n\nThe `multi_producer_sequencer` class is a synchronization primitive that coordinates\naccess to a ring-buffer for multiple producers and one or more consumers.\n\nFor a single-producer variant see the `single_producer_sequencer` class.\n\nNote that the ring-buffer must have a size that is a power-of-two. This is because\nthe implementation uses bitmasks instead of integer division/modulo to calculate\nthe offset into the buffer. Also, this allows the sequence number to safely wrap\naround the 32-bit/64-bit value.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/multi_producer_sequencer.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename SEQUENCE = std::size_t,\n           typename TRAITS = sequence_traits\u003cSEQUENCE\u003e\u003e\n  class multi_producer_sequencer\n  {\n  public:\n    multi_producer_sequencer(\n      const sequence_barrier\u003cSEQUENCE, TRAITS\u003e\u0026 consumerBarrier,\n      SEQUENCE initialSequence = TRAITS::initial_sequence);\n\n    std::size_t buffer_size() const noexcept;\n\n    // Consumer interface\n    //\n    // Each consumer keeps track of their own 'lastKnownPublished' value\n    // and must pass this to the methods that query for an updated last-known\n    // published sequence number.\n\n    SEQUENCE last_published_after(SEQUENCE lastKnownPublished) const noexcept;\n\n    template\u003ctypename SCHEDULER\u003e\n    Awaitable\u003cSEQUENCE\u003e wait_until_published(\n      SEQUENCE targetSequence,\n      SEQUENCE lastKnownPublished,\n      SCHEDULER\u0026 scheduler) const noexcept;\n\n    // Producer interface\n\n    // Query whether any slots available for claiming (approx.)\n    bool any_available() const noexcept;\n\n    template\u003ctypename SCHEDULER\u003e\n    Awaitable\u003cSEQUENCE\u003e claim_one(SCHEDULER\u0026 scheduler) noexcept;\n\n    template\u003ctypename SCHEDULER\u003e\n    Awaitable\u003csequence_range\u003cSEQUENCE, TRAITS\u003e\u003e claim_up_to(\n      std::size_t count,\n      SCHEDULER\u0026 scheduler) noexcept;\n\n    // Mark the specified sequence number as published.\n    void publish(SEQUENCE sequence) noexcept;\n\n    // Mark all sequence numbers in the specified range as published.\n    void publish(const sequence_range\u003cSEQUENCE, TRAITS\u003e\u0026 range) noexcept;\n  };\n}\n```\n\n## Cancellation\n\nA `cancellation_token` is a value that can be passed to a function that allows the caller to subsequently communicate a request to cancel the operation to that function.\n\nTo obtain a `cancellation_token` that is able to be cancelled you must first create a `cancellation_source` object.\nThe `cancellation_source::token()` method can be used to manufacture new `cancellation_token` values that are linked to that `cancellation_source` object.\n\nWhen you want to later request cancellation of an operation you have passed a `cancellation_token` to\nyou can call `cancellation_source::request_cancellation()` on an associated `cancellation_source` object.\n\nFunctions can respond to a request for cancellation in one of two ways:\n1. Poll for cancellation at regular intervals by calling either `cancellation_token::is_cancellation_requested()` or `cancellation_token::throw_if_cancellation_requested()`.\n2. Register a callback to be executed when cancellation is requested using the `cancellation_registration` class.\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n  class cancellation_source\n  {\n  public:\n    // Construct a new, independently cancellable cancellation source.\n    cancellation_source();\n\n    // Construct a new reference to the same cancellation state.\n    cancellation_source(const cancellation_source\u0026 other) noexcept;\n    cancellation_source(cancellation_source\u0026\u0026 other) noexcept;\n\n    ~cancellation_source();\n\n    cancellation_source\u0026 operator=(const cancellation_source\u0026 other) noexcept;\n    cancellation_source\u0026 operator=(cancellation_source\u0026\u0026 other) noexcept;\n\n    bool is_cancellation_requested() const noexcept;\n    bool can_be_cancelled() const noexcept;\n    void request_cancellation();\n\n    cancellation_token token() const noexcept;\n  };\n\n  class cancellation_token\n  {\n  public:\n    // Construct a token that can't be cancelled.\n    cancellation_token() noexcept;\n\n    cancellation_token(const cancellation_token\u0026 other) noexcept;\n    cancellation_token(cancellation_token\u0026\u0026 other) noexcept;\n\n    ~cancellation_token();\n\n    cancellation_token\u0026 operator=(const cancellation_token\u0026 other) noexcept;\n    cancellation_token\u0026 operator=(cancellation_token\u0026\u0026 other) noexcept;\n\n    bool is_cancellation_requested() const noexcept;\n    void throw_if_cancellation_requested() const;\n\n    // Query if this token can ever have cancellation requested.\n    // Code can use this to take a more efficient code-path in cases\n    // that the operation does not need to handle cancellation.\n    bool can_be_cancelled() const noexcept;\n  };\n\n  // RAII class for registering a callback to be executed if cancellation\n  // is requested on a particular cancellation token.\n  class cancellation_registration\n  {\n  public:\n\n    // Register a callback to be executed if cancellation is requested.\n    // Callback will be called with no arguments on the thread that calls\n    // request_cancellation() if cancellation is not yet requested, or\n    // called immediately if cancellation has already been requested.\n    // Callback must not throw an unhandled exception when called.\n    template\u003ctypename CALLBACK\u003e\n    cancellation_registration(cancellation_token token, CALLBACK\u0026\u0026 callback);\n\n    cancellation_registration(const cancellation_registration\u0026 other) = delete;\n\n    ~cancellation_registration();\n  };\n\n  class operation_cancelled : public std::exception\n  {\n  public:\n    operation_cancelled();\n    const char* what() const override;\n  };\n}\n```\n\nExample: Polling Approach\n```c++\ncppcoro::task\u003c\u003e do_something_async(cppcoro::cancellation_token token)\n{\n  // Explicitly define cancellation points within the function\n  // by calling throw_if_cancellation_requested().\n  token.throw_if_cancellation_requested();\n\n  co_await do_step_1();\n\n  token.throw_if_cancellation_requested();\n\n  do_step_2();\n\n  // Alternatively, you can query if cancellation has been\n  // requested to allow yourself to do some cleanup before\n  // returning.\n  if (token.is_cancellation_requested())\n  {\n    display_message_to_user(\"Cancelling operation...\");\n    do_cleanup();\n    throw cppcoro::operation_cancelled{};\n  }\n\n  do_final_step();\n}\n```\n\nExample: Callback Approach\n```c++\n// Say we already have a timer abstraction that supports being\n// cancelled but it doesn't support cancellation_tokens natively.\n// You can use a cancellation_registration to register a callback\n// that calls the existing cancellation API. e.g.\ncppcoro::task\u003c\u003e cancellable_timer_wait(cppcoro::cancellation_token token)\n{\n  auto timer = create_timer(10s);\n\n  cppcoro::cancellation_registration registration(token, [\u0026]\n  {\n    // Call existing timer cancellation API.\n    timer.cancel();\n  });\n\n  co_await timer;\n}\n```\n\n## `static_thread_pool`\n\nThe `static_thread_pool` class provides an abstraction that lets you schedule work\non a fixed-size pool of threads.\n\nThis class implements the **Scheduler** concept (see below).\n\nYou can enqueue work to the thread-pool by executing `co_await threadPool.schedule()`.\nThis operation will suspend the current coroutine, enqueue it for execution on the\nthread-pool and the thread pool will then resume the coroutine when a thread in the\nthread-pool is next free to run the coroutine. **This operation is guaranteed not\nto throw and, in the common case, will not allocate any memory**.\n\nThis class makes use of a work-stealing algorithm to load-balance work across multiple\nthreads. Work enqueued to the thread-pool from a thread-pool thread will be scheduled\nfor execution on the same thread in a LIFO queue. Work enqueued to the thread-pool from\na remote thread will be enqueued to a global FIFO queue. When a worker thread runs out\nof work from its local queue it first tries to dequeue work from the global queue. If\nthat queue is empty then it next tries to steal work from the back of the queues of\nthe other worker threads.\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n  class static_thread_pool\n  {\n  public:\n    // Initialise the thread-pool with a number of threads equal to\n    // std::thread::hardware_concurrency().\n    static_thread_pool();\n\n    // Initialise the thread pool with the specified number of threads.\n    explicit static_thread_pool(std::uint32_t threadCount);\n\n    std::uint32_t thread_count() const noexcept;\n\n    class schedule_operation\n    {\n    public:\n      schedule_operation(static_thread_pool* tp) noexcept;\n\n      bool await_ready() noexcept;\n      bool await_suspend(std::experimental::coroutine_handle\u003c\u003e h) noexcept;\n      bool await_resume() noexcept;\n\n    private:\n      // unspecified\n    };\n\n    // Return an operation that can be awaited by a coroutine.\n    //\n    //\n    [[nodiscard]]\n    schedule_operation schedule() noexcept;\n\n  private:\n\n    // Unspecified\n\n  };\n}\n```\n\nExample usage: Simple\n```c++\ncppcoro::task\u003cstd::string\u003e do_something_on_threadpool(cppcoro::static_thread_pool\u0026 tp)\n{\n  // First schedule the coroutine onto the threadpool.\n  co_await tp.schedule();\n\n  // When it resumes, this coroutine is now running on the threadpool.\n  do_something();\n}\n```\n\nExample usage: Doing things in parallel - using `schedule_on()` operator with `static_thread_pool`.\n```c++\ncppcoro::task\u003cdouble\u003e dot_product(static_thread_pool\u0026 tp, double a[], double b[], size_t count)\n{\n  if (count \u003e 1000)\n  {\n    // Subdivide the work recursively into two equal tasks\n    // The first half is scheduled to the thread pool so it can run concurrently\n    // with the second half which continues on this thread.\n    size_t halfCount = count / 2;\n    auto [first, second] = co_await when_all(\n      schedule_on(tp, dot_product(tp, a, b, halfCount),\n      dot_product(tp, a + halfCount, b + halfCount, count - halfCount));\n    co_return first + second;\n  }\n  else\n  {\n    double sum = 0.0;\n    for (size_t i = 0; i \u003c count; ++i)\n    {\n      sum += a[i] * b[i];\n    }\n    co_return sum;\n  }\n}\n```\n\n## `io_service` and `io_work_scope`\n\nThe `io_service` class provides an abstraction for processing I/O completion events\nfrom asynchronous I/O operations.\n\nWhen an asynchronous I/O operation completes, the coroutine that was awaiting\nthat operation will be resumed on an I/O thread inside a call to one of the\nevent-processing methods: `process_events()`, `process_pending_events()`,\n`process_one_event()` or `process_one_pending_event()`.\n\nThe `io_service` class does not manage any I/O threads.\nYou must ensure that some thread calls one of the event-processing methods for coroutines awaiting I/O\ncompletion events to be dispatched. This can either be a dedicated thread that calls `process_events()`\nor mixed in with some other event loop (e.g. a UI event loop) by periodically polling for new events\nvia a call to `process_pending_events()` or `process_one_pending_event()`.\n\nThis allows integration of the `io_service` event-loop with other event loops, such as a user-interface event loop.\n\nYou can multiplex processing of events across multiple threads by having multiple threads call\n`process_events()`. You can specify a hint as to the maximum number of threads to have actively\nprocessing events via an optional `io_service` constructor parameter.\n\nOn Windows, the implementation makes use of the Windows I/O Completion Port facility to dispatch\nevents to I/O threads in a scalable manner.\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n  class io_service\n  {\n  public:\n\n    class schedule_operation;\n    class timed_schedule_operation;\n\n    io_service();\n    io_service(std::uint32_t concurrencyHint);\n\n    io_service(io_service\u0026\u0026) = delete;\n    io_service(const io_service\u0026) = delete;\n    io_service\u0026 operator=(io_service\u0026\u0026) = delete;\n    io_service\u0026 operator=(const io_service\u0026) = delete;\n\n    ~io_service();\n\n    // Scheduler methods\n\n    [[nodiscard]]\n    schedule_operation schedule() noexcept;\n\n    template\u003ctypename REP, typename RATIO\u003e\n    [[nodiscard]]\n    timed_schedule_operation schedule_after(\n      std::chrono::duration\u003cREP, RATIO\u003e delay,\n      cppcoro::cancellation_token cancellationToken = {}) noexcept;\n\n    // Event-loop methods\n    //\n    // I/O threads must call these to process I/O events and execute\n    // scheduled coroutines.\n\n    std::uint64_t process_events();\n    std::uint64_t process_pending_events();\n    std::uint64_t process_one_event();\n    std::uint64_t process_one_pending_event();\n\n    // Request that all threads processing events exit their event loops.\n    void stop() noexcept;\n\n    // Query if some thread has called stop()\n    bool is_stop_requested() const noexcept;\n\n    // Reset the event-loop after a call to stop() so that threads can\n    // start processing events again.\n    void reset();\n\n    // Reference-counting methods for tracking outstanding references\n    // to the io_service.\n    //\n    // The io_service::stop() method will be called when the last work\n    // reference is decremented.\n    //\n    // Use the io_work_scope RAII class to manage calling these methods on\n    // entry-to and exit-from a scope.\n    void notify_work_started() noexcept;\n    void notify_work_finished() noexcept;\n\n  };\n\n  class io_service::schedule_operation\n  {\n  public:\n    schedule_operation(const schedule_operation\u0026) noexcept;\n    schedule_operation\u0026 operator=(const schedule_operation\u0026) noexcept;\n\n    bool await_ready() const noexcept;\n    void await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter) noexcept;\n    void await_resume() noexcept;\n  };\n\n  class io_service::timed_schedule_operation\n  {\n  public:\n    timed_schedule_operation(timed_schedule_operation\u0026\u0026) noexcept;\n\n    timed_schedule_operation(const timed_schedule_operation\u0026) = delete;\n    timed_schedule_operation\u0026 operator=(const timed_schedule_operation\u0026) = delete;\n    timed_schedule_operation\u0026 operator=(timed_schedule_operation\u0026\u0026) = delete;\n\n    bool await_ready() const noexcept;\n    void await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter);\n    void await_resume();\n  };\n\n  class io_work_scope\n  {\n  public:\n\n    io_work_scope(io_service\u0026 ioService) noexcept;\n\n    io_work_scope(const io_work_scope\u0026 other) noexcept;\n    io_work_scope(io_work_scope\u0026\u0026 other) noexcept;\n\n    ~io_work_scope();\n\n    io_work_scope\u0026 operator=(const io_work_scope\u0026 other) noexcept;\n    io_work_scope\u0026 operator=(io_work_scope\u0026\u0026 other) noexcept;\n\n    io_service\u0026 service() const noexcept;\n  };\n\n}\n```\n\nExample:\n```c++\n#include \u003ccppcoro/task.hpp\u003e\n#include \u003ccppcoro/task.hpp\u003e\n#include \u003ccppcoro/io_service.hpp\u003e\n#include \u003ccppcoro/read_only_file.hpp\u003e\n\n#include \u003cexperimental/filesystem\u003e\n#include \u003cmemory\u003e\n#include \u003calgorithm\u003e\n#include \u003ciostream\u003e\n\nnamespace fs = std::experimental::filesystem;\n\ncppcoro::task\u003cstd::uint64_t\u003e count_lines(cppcoro::io_service\u0026 ioService, fs::path path)\n{\n  auto file = cppcoro::read_only_file::open(ioService, path);\n\n  constexpr size_t bufferSize = 4096;\n  auto buffer = std::make_unique\u003cstd::uint8_t[]\u003e(bufferSize);\n\n  std::uint64_t newlineCount = 0;\n\n  for (std::uint64_t offset = 0, fileSize = file.size(); offset \u003c fileSize;)\n  {\n    const auto bytesToRead = static_cast\u003csize_t\u003e(\n      std::min\u003cstd::uint64_t\u003e(bufferSize, fileSize - offset));\n\n    const auto bytesRead = co_await file.read(offset, buffer.get(), bytesToRead);\n\n    newlineCount += std::count(buffer.get(), buffer.get() + bytesRead, '\\n');\n\n    offset += bytesRead;\n  }\n\n  co_return newlineCount;\n}\n\ncppcoro::task\u003c\u003e run(cppcoro::io_service\u0026 ioService)\n{\n  cppcoro::io_work_scope ioScope(ioService);\n\n  auto lineCount = co_await count_lines(ioService, fs::path{\"foo.txt\"});\n\n  std::cout \u003c\u003c \"foo.txt has \" \u003c\u003c lineCount \u003c\u003c \" lines.\" \u003c\u003c std::endl;;\n}\n\ncppcoro::task\u003c\u003e process_events(cppcoro::io_service\u0026 ioService)\n{\n  // Process events until the io_service is stopped.\n  // ie. when the last io_work_scope goes out of scope.\n  ioService.process_events();\n  co_return;\n}\n\nint main()\n{\n  cppcoro::io_service ioService;\n\n  cppcoro::sync_wait(cppcoro::when_all_ready(\n    run(ioService),\n    process_events(ioService)));\n\n  return 0;\n}\n```\n\n### `io_service` as a scheduler\n\nAn `io_service` class implements the interfaces for the `Scheduler` and `DelayedScheduler` concepts.\n\nThis allows a coroutine to suspend execution on the current thread and schedule itself for resumption\non an I/O thread associated with a particular `io_service` object.\n\nExample:\n```c++\ncppcoro::task\u003c\u003e do_something(cppcoro::io_service\u0026 ioService)\n{\n  // Coroutine starts execution on the thread of the task awaiter.\n\n  // A coroutine can transfer execution to an I/O thread by awaiting the\n  // result of io_service::schedule().\n  co_await ioService.schedule();\n\n  // At this point, the coroutine is now executing on an I/O thread\n  // inside a call to one of the io_service event processing methods.\n\n  // A coroutine can also perform a delayed-schedule that will suspend\n  // the coroutine for a specified duration of time before scheduling\n  // it for resumption on an I/O thread.\n  co_await ioService.schedule_after(100ms);\n\n  // At this point, the coroutine is executing on a potentially different I/O thread.\n}\n```\n\n## `file`, `readable_file`, `writable_file`\n\nThese types are abstract base-classes for performing concrete file I/O.\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n  class file_read_operation;\n  class file_write_operation;\n\n  class file\n  {\n  public:\n\n    virtual ~file();\n\n    std::uint64_t size() const;\n\n  protected:\n\n    file(file\u0026\u0026 other) noexcept;\n\n  };\n\n  class readable_file : public virtual file\n  {\n  public:\n\n    [[nodiscard]]\n    file_read_operation read(\n      std::uint64_t offset,\n      void* buffer,\n      std::size_t byteCount,\n      cancellation_token ct = {}) const noexcept;\n\n  };\n\n  class writable_file : public virtual file\n  {\n  public:\n\n    void set_size(std::uint64_t fileSize);\n\n    [[nodiscard]]\n    file_write_operation write(\n      std::uint64_t offset,\n      const void* buffer,\n      std::size_t byteCount,\n      cancellation_token ct = {}) noexcept;\n\n  };\n\n  class file_read_operation\n  {\n  public:\n\n    file_read_operation(file_read_operation\u0026\u0026 other) noexcept;\n\n    bool await_ready() const noexcept;\n    bool await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter);\n    std::size_t await_resume();\n\n  };\n\n  class file_write_operation\n  {\n  public:\n\n    file_write_operation(file_write_operation\u0026\u0026 other) noexcept;\n\n    bool await_ready() const noexcept;\n    bool await_suspend(std::experimental::coroutine_handle\u003c\u003e awaiter);\n    std::size_t await_resume();\n\n  };\n}\n```\n\n## `read_only_file`, `write_only_file`, `read_write_file`\n\nThese types represent concrete file I/O classes.\n\nAPI Summary:\n```c++\nnamespace cppcoro\n{\n  class read_only_file : public readable_file\n  {\n  public:\n\n    [[nodiscard]]\n    static read_only_file open(\n      io_service\u0026 ioService,\n      const std::experimental::filesystem::path\u0026 path,\n      file_share_mode shareMode = file_share_mode::read,\n      file_buffering_mode bufferingMode = file_buffering_mode::default_);\n\n  };\n\n  class write_only_file : public writable_file\n  {\n  public:\n\n    [[nodiscard]]\n    static write_only_file open(\n      io_service\u0026 ioService,\n      const std::experimental::filesystem::path\u0026 path,\n      file_open_mode openMode = file_open_mode::create_or_open,\n      file_share_mode shareMode = file_share_mode::none,\n      file_buffering_mode bufferingMode = file_buffering_mode::default_);\n\n  };\n\n  class read_write_file : public readable_file, public writable_file\n  {\n  public:\n\n    [[nodiscard]]\n    static read_write_file open(\n      io_service\u0026 ioService,\n      const std::experimental::filesystem::path\u0026 path,\n      file_open_mode openMode = file_open_mode::create_or_open,\n      file_share_mode shareMode = file_share_mode::none,\n      file_buffering_mode bufferingMode = file_buffering_mode::default_);\n\n  };\n}\n```\n\nAll `open()` functions throw `std::system_error` on failure.\n\n# Networking\n\nNOTE: Networking abstractions are currently only supported on the Windows platform.\nLinux support will be coming soon.\n\n## `socket`\n\nThe socket class can be used to send/receive data over the network asynchronously.\n\nCurrently only supports TCP/IP, UDP/IP over IPv4 and IPv6.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/net/socket.hpp\u003e\nnamespace cppcoro::net\n{\n  class socket\n  {\n  public:\n\n    static socket create_tcpv4(ip_service\u0026 ioSvc);\n    static socket create_tcpv6(ip_service\u0026 ioSvc);\n    static socket create_updv4(ip_service\u0026 ioSvc);\n    static socket create_udpv6(ip_service\u0026 ioSvc);\n\n    socket(socket\u0026\u0026 other) noexcept;\n\n    ~socket();\n\n    socket\u0026 operator=(socket\u0026\u0026 other) noexcept;\n\n    // Return the native socket handle for the socket\n    \u003cplatform-specific\u003e native_handle() noexcept;\n\n    const ip_endpoint\u0026 local_endpoint() const noexcept;\n    const ip_endpoint\u0026 remote_endpoint() const noexcept;\n\n    void bind(const ip_endpoint\u0026 localEndPoint);\n\n    void listen();\n\n    [[nodiscard]]\n    Awaitable\u003cvoid\u003e connect(const ip_endpoint\u0026 remoteEndPoint) noexcept;\n    [[nodiscard]]\n    Awaitable\u003cvoid\u003e connect(const ip_endpoint\u0026 remoteEndPoint,\n                            cancellation_token ct) noexcept;\n\n    [[nodiscard]]\n    Awaitable\u003cvoid\u003e accept(socket\u0026 acceptingSocket) noexcept;\n    [[nodiscard]]\n    Awaitable\u003cvoid\u003e accept(socket\u0026 acceptingSocket,\n                           cancellation_token ct) noexcept;\n\n    [[nodiscard]]\n    Awaitable\u003cvoid\u003e disconnect() noexcept;\n    [[nodiscard]]\n    Awaitable\u003cvoid\u003e disconnect(cancellation_token ct) noexcept;\n\n    [[nodiscard]]\n    Awaitable\u003cstd::size_t\u003e send(const void* buffer, std::size_t size) noexcept;\n    [[nodiscard]]\n    Awaitable\u003cstd::size_t\u003e send(const void* buffer,\n                                std::size_t size,\n                                cancellation_token ct) noexcept;\n\n    [[nodiscard]]\n    Awaitable\u003cstd::size_t\u003e recv(void* buffer, std::size_t size) noexcept;\n    [[nodiscard]]\n    Awaitable\u003cstd::size_t\u003e recv(void* buffer,\n                                std::size_t size,\n                                cancellation_token ct) noexcept;\n\n    [[nodiscard]]\n    socket_recv_from_operation recv_from(\n        void* buffer,\n        std::size_t size) noexcept;\n    [[nodiscard]]\n    socket_recv_from_operation_cancellable recv_from(\n        void* buffer,\n        std::size_t size,\n        cancellation_token ct) noexcept;\n\n    [[nodiscard]]\n    socket_send_to_operation send_to(\n        const ip_endpoint\u0026 destination,\n        const void* buffer,\n        std::size_t size) noexcept;\n    [[nodiscard]]\n    socket_send_to_operation_cancellable send_to(\n        const ip_endpoint\u0026 destination,\n        const void* buffer,\n        std::size_t size,\n        cancellation_token ct) noexcept;\n\n    void close_send();\n    void close_recv();\n\n  };\n}\n```\n\nExample: Echo Server\n```c++\n#include \u003ccppcoro/net/socket.hpp\u003e\n#include \u003ccppcoro/io_service.hpp\u003e\n#include \u003ccppcoro/cancellation_source.hpp\u003e\n#include \u003ccppcoro/async_scope.hpp\u003e\n#include \u003ccppcoro/on_scope_exit.hpp\u003e\n\n#include \u003cmemory\u003e\n#include \u003ciostream\u003e\n\ncppcoro::task\u003cvoid\u003e handle_connection(socket s)\n{\n  try\n  {\n    const size_t bufferSize = 16384;\n    auto buffer = std::make_unique\u003cunsigned char[]\u003e(bufferSize);\n    size_t bytesRead;\n    do {\n      // Read some bytes\n      bytesRead = co_await s.recv(buffer.get(), bufferSize);\n\n      // Write some bytes\n      size_t bytesWritten = 0;\n      while (bytesWritten \u003c bytesRead) {\n        bytesWritten += co_await s.send(\n          buffer.get() + bytesWritten,\n          bytesRead - bytesWritten);\n      }\n    } while (bytesRead != 0);\n\n    s.close_send();\n\n    co_await s.disconnect();\n  }\n  catch (...)\n  {\n    std::cout \u003c\u003c \"connection failed\" \u003c\u003c std::\n  }\n}\n\ncppcoro::task\u003cvoid\u003e echo_server(\n  cppcoro::net::ipv4_endpoint endpoint,\n  cppcoro::io_service\u0026 ioSvc,\n  cancellation_token ct)\n{\n  cppcoro::async_scope scope;\n\n  std::exception_ptr ex;\n  try\n  {\n    auto listeningSocket = cppcoro::net::socket::create_tcpv4(ioSvc);\n    listeningSocket.bind(endpoint);\n    listeningSocket.listen();\n\n    while (true) {\n      auto connection = cppcoro::net::socket::create_tcpv4(ioSvc);\n      co_await listeningSocket.accept(connection, ct);\n      scope.spawn(handle_connection(std::move(connection)));\n    }\n  }\n  catch (cppcoro::operation_cancelled)\n  {\n  }\n  catch (...)\n  {\n    ex = std::current_exception();\n  }\n\n  // Wait until all handle_connection tasks have finished.\n  co_await scope.join();\n\n  if (ex) std::rethrow_exception(ex);\n}\n\nint main(int argc, const char* argv[])\n{\n    cppcoro::io_service ioSvc;\n\n    if (argc != 2) return -1;\n\n    auto endpoint = cppcoro::ipv4_endpoint::from_string(argv[1]);\n    if (!endpoint) return -1;\n\n    (void)cppcoro::sync_wait(cppcoro::when_all(\n        [\u0026]() -\u003e task\u003c\u003e\n        {\n            // Shutdown the event loop once finished.\n            auto stopOnExit = cppcoro::on_scope_exit([\u0026] { ioSvc.stop(); });\n\n            cppcoro::cancellation_source canceller;\n            co_await cppcoro::when_all(\n                [\u0026]() -\u003e task\u003c\u003e\n                {\n                    // Run for 30s then stop accepting new connections.\n                    co_await ioSvc.schedule_after(std::chrono::seconds(30));\n                    canceller.request_cancellation();\n                }(),\n                echo_server(*endpoint, ioSvc, canceller.token()));\n        }(),\n        [\u0026]() -\u003e task\u003c\u003e\n        {\n            ioSvc.process_events();\n        }()));\n\n    return 0;\n}\n```\n\n## `ip_address`, `ipv4_address`, `ipv6_address`\n\nHelper classes for representing an IP address.\n\nAPI Synopsis:\n```c++\nnamespace cppcoro::net\n{\n  class ipv4_address\n  {\n    using bytes_t = std::uint8_t[4];\n  public:\n    constexpr ipv4_address();\n    explicit constexpr ipv4_address(std::uint32_t integer);\n    explicit constexpr ipv4_address(const std::uint8_t(\u0026bytes)[4]);\n    explicit constexpr ipv4_address(std::uint8_t b0,\n                                    std::uint8_t b1,\n                                    std::uint8_t b2,\n                                    std::uint8_t b3);\n\n    constexpr const bytes_t\u0026 bytes() const;\n\n    constexpr std::uint32_t to_integer() const;\n\n    static constexpr ipv4_address loopback();\n\n    constexpr bool is_loopback() const;\n    constexpr bool is_private_network() const;\n\n    constexpr bool operator==(ipv4_address other) const;\n    constexpr bool operator!=(ipv4_address other) const;\n    constexpr bool operator\u003c(ipv4_address other) const;\n    constexpr bool operator\u003e(ipv4_address other) const;\n    constexpr bool operator\u003c=(ipv4_address other) const;\n    constexpr bool operator\u003e=(ipv4_address other) const;\n\n    std::string to_string();\n\n    static std::optional\u003cipv4_address\u003e from_string(std::string_view string) noexcept;\n  };\n\n  class ipv6_address\n  {\n    using bytes_t = std::uint8_t[16];\n  public:\n    constexpr ipv6_address();\n\n    explicit constexpr ipv6_address(\n      std::uint64_t subnetPrefix,\n      std::uint64_t interfaceIdentifier);\n\n    constexpr ipv6_address(\n      std::uint16_t part0,\n      std::uint16_t part1,\n      std::uint16_t part2,\n      std::uint16_t part3,\n      std::uint16_t part4,\n      std::uint16_t part5,\n      std::uint16_t part6,\n      std::uint16_t part7);\n\n    explicit constexpr ipv6_address(\n        const std::uint16_t(\u0026parts)[8]);\n\n    explicit constexpr ipv6_address(\n        const std::uint8_t(bytes)[16]);\n\n    constexpr const bytes_t\u0026 bytes() const;\n\n    constexpr std::uint64_t subnet_prefix() const;\n    constexpr std::uint64_t interface_identifier() const;\n\n    static constexpr ipv6_address unspecified();\n    static constexpr ipv6_address loopback();\n\n    static std::optional\u003cipv6_address\u003e from_string(std::string_view string) noexcept;\n\n    std::string to_string() const;\n\n    constexpr bool operator==(const ipv6_address\u0026 other) const;\n    constexpr bool operator!=(const ipv6_address\u0026 other) const;\n    constexpr bool operator\u003c(const ipv6_address\u0026 other) const;\n    constexpr bool operator\u003e(const ipv6_address\u0026 other) const;\n    constexpr bool operator\u003c=(const ipv6_address\u0026 other) const;\n    constexpr bool operator\u003e=(const ipv6_address\u0026 other) const;\n\n  };\n\n  class ip_address\n  {\n  public:\n\n    // Constructs to IPv4 address 0.0.0.0\n    ip_address() noexcept;\n\n    ip_address(ipv4_address address) noexcept;\n    ip_address(ipv6_address address) noexcept;\n\n    bool is_ipv4() const noexcept;\n    bool is_ipv6() const noexcept;\n\n    const ipv4_address\u0026 to_ipv4() const;\n    const ipv6_address\u0026 to_ipv6() const;\n\n    const std::uint8_t* bytes() const noexcept;\n\n    std::string to_string() const;\n\n    static std::optional\u003cip_address\u003e from_string(std::string_view string) noexcept;\n\n    bool operator==(const ip_address\u0026 rhs) const noexcept;\n    bool operator!=(const ip_address\u0026 rhs) const noexcept;\n\n    //  ipv4_address sorts less than ipv6_address\n    bool operator\u003c(const ip_address\u0026 rhs) const noexcept;\n    bool operator\u003e(const ip_address\u0026 rhs) const noexcept;\n    bool operator\u003c=(const ip_address\u0026 rhs) const noexcept;\n    bool operator\u003e=(const ip_address\u0026 rhs) const noexcept;\n\n  };\n}\n```\n\n## `ip_endpoint`, `ipv4_endpoint` `ipv6_endpoint`\n\nHelper classes for representing an IP address and port-number.\n\nAPI Synopsis:\n```c++\nnamespace cppcoro::net\n{\n  class ipv4_endpoint\n  {\n  public:\n    ipv4_endpoint() noexcept;\n    explicit ipv4_endpoint(ipv4_address address, std::uint16_t port = 0) noexcept;\n\n    const ipv4_address\u0026 address() const noexcept;\n    std::uint16_t port() const noexcept;\n\n    std::string to_string() const;\n    static std::optional\u003cipv4_endpoint\u003e from_string(std::string_view string) noexcept;\n  };\n\n  bool operator==(const ipv4_endpoint\u0026 a, const ipv4_endpoint\u0026 b);\n  bool operator!=(const ipv4_endpoint\u0026 a, const ipv4_endpoint\u0026 b);\n  bool operator\u003c(const ipv4_endpoint\u0026 a, const ipv4_endpoint\u0026 b);\n  bool operator\u003e(const ipv4_endpoint\u0026 a, const ipv4_endpoint\u0026 b);\n  bool operator\u003c=(const ipv4_endpoint\u0026 a, const ipv4_endpoint\u0026 b);\n  bool operator\u003e=(const ipv4_endpoint\u0026 a, const ipv4_endpoint\u0026 b);\n\n  class ipv6_endpoint\n  {\n  public:\n    ipv6_endpoint() noexcept;\n    explicit ipv6_endpoint(ipv6_address address, std::uint16_t port = 0) noexcept;\n\n    const ipv6_address\u0026 address() const noexcept;\n    std::uint16_t port() const noexcept;\n\n    std::string to_string() const;\n    static std::optional\u003cipv6_endpoint\u003e from_string(std::string_view string) noexcept;\n  };\n\n  bool operator==(const ipv6_endpoint\u0026 a, const ipv6_endpoint\u0026 b);\n  bool operator!=(const ipv6_endpoint\u0026 a, const ipv6_endpoint\u0026 b);\n  bool operator\u003c(const ipv6_endpoint\u0026 a, const ipv6_endpoint\u0026 b);\n  bool operator\u003e(const ipv6_endpoint\u0026 a, const ipv6_endpoint\u0026 b);\n  bool operator\u003c=(const ipv6_endpoint\u0026 a, const ipv6_endpoint\u0026 b);\n  bool operator\u003e=(const ipv6_endpoint\u0026 a, const ipv6_endpoint\u0026 b);\n\n  class ip_endpoint\n  {\n  public:\n     // Constructs to IPv4 end-point 0.0.0.0:0\n     ip_endpoint() noexcept;\n\n     ip_endpoint(ipv4_endpoint endpoint) noexcept;\n     ip_endpoint(ipv6_endpoint endpoint) noexcept;\n\n     bool is_ipv4() const noexcept;\n     bool is_ipv6() const noexcept;\n\n     const ipv4_endpoint\u0026 to_ipv4() const;\n     const ipv6_endpoint\u0026 to_ipv6() const;\n\n     ip_address address() const noexcept;\n     std::uint16_t port() const noexcept;\n\n     std::string to_string() const;\n\n     static std::optional\u003cip_endpoint\u003e from_string(std::string_view string) noexcept;\n\n     bool operator==(const ip_endpoint\u0026 rhs) const noexcept;\n     bool operator!=(const ip_endpoint\u0026 rhs) const noexcept;\n\n     //  ipv4_endpoint sorts less than ipv6_endpoint\n     bool operator\u003c(const ip_endpoint\u0026 rhs) const noexcept;\n     bool operator\u003e(const ip_endpoint\u0026 rhs) const noexcept;\n     bool operator\u003c=(const ip_endpoint\u0026 rhs) const noexcept;\n     bool operator\u003e=(const ip_endpoint\u0026 rhs) const noexcept;\n  };\n}\n```\n\n# Functions\n\n## `sync_wait()`\n\nThe `sync_wait()` function can be used to synchronously wait until the specified `awaitable`\ncompletes.\n\nThe specified awaitable will be `co_await`ed on current thread inside a newly created coroutine.\n\nThe `sync_wait()` call will block until the operation completes and will return the result of\nthe `co_await` expression or rethrow the exception if the `co_await` expression completed with\nan unhandled exception.\n\nThe `sync_wait()` function is mostly useful for starting a top-level task from within `main()`\nand waiting until the task finishes, in practice it is the only way to start the first/top-level\n`task`.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/sync_wait.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename AWAITABLE\u003e\n  auto sync_wait(AWAITABLE\u0026\u0026 awaitable)\n    -\u003e typename awaitable_traits\u003cAWAITABLE\u0026\u0026\u003e::await_result_t;\n}\n```\n\nExamples:\n```c++\nvoid example_task()\n{\n  auto makeTask = []() -\u003e task\u003cstd::string\u003e\n  {\n    co_return \"foo\";\n  };\n\n  auto task = makeTask();\n\n  // start the lazy task and wait until it completes\n  sync_wait(task); // -\u003e \"foo\"\n  sync_wait(makeTask()); // -\u003e \"foo\"\n}\n\nvoid example_shared_task()\n{\n  auto makeTask = []() -\u003e shared_task\u003cstd::string\u003e\n  {\n    co_return \"foo\";\n  };\n\n  auto task = makeTask();\n  // start the shared task and wait until it completes\n  sync_wait(task) == \"foo\";\n  sync_wait(makeTask()) == \"foo\";\n}\n```\n\n## `when_all_ready()`\n\nThe `when_all_ready()` function can be used to create a new awaitable that completes when\nall of the input awaitables complete.\n\nInput tasks can be any type of awaitable.\n\nWhen the returned awaitable is `co_await`ed it will `co_await` each of the input awaitables\nin turn on the awaiting thread in the order they are passed to the `when_all_ready()`\nfunction. If these tasks to not complete synchronously then they will execute concurrently.\n\nOnce all of the `co_await` expressions on input awaitables have run to completion the\nreturned awaitable will complete and resume the awaiting coroutine. The awaiting coroutine\nwill be resumed on the thread of the input awaitable that is last to complete.\n\nThe returned awaitable is guaranteed not to throw an exception when `co_await`ed,\neven if some of the input awaitables fail with an unhandled exception.\n\nNote, however, that the `when_all_ready()` call itself may throw `std::bad_alloc` if it\nwas unable to allocate memory for the coroutine frames required to await each of the\ninput awaitables. It may also throw an exception if any of the input awaitable objects\nthrow from their copy/move constructors.\n\nThe result of `co_await`ing the returned awaitable is a `std::tuple` or `std::vector`\nof `when_all_task\u003cRESULT\u003e` objects. These objects allow you to obtain the result (or exception)\nof each input awaitable separately by calling the `when_all_task\u003cRESULT\u003e::result()`\nmethod of the corresponding output task.\nThis allows the caller to concurrently await multiple awaitables and synchronize on\ntheir completion while still retaining the ability to subsequently inspect the results of\neach of the `co_await` operations for success/failure.\n\nThis differs from `when_all()` where the failure of any individual `co_await` operation\ncauses the overall operation to fail with an exception. This means you cannot determine\nwhich of the component `co_await` operations failed and also prevents you from obtaining\nthe results of the other `co_await` operations.\n\nAPI summary:\n```c++\n// \u003ccppcoro/when_all_ready.hpp\u003e\nnamespace cppcoro\n{\n  // Concurrently await multiple awaitables.\n  //\n  // Returns an awaitable object that, when co_await'ed, will co_await each of the input\n  // awaitable objects and will resume the awaiting coroutine only when all of the\n  // component co_await operations complete.\n  //\n  // Result of co_await'ing the returned awaitable is a std::tuple of detail::when_all_task\u003cT\u003e,\n  // one for each input awaitable and where T is the result-type of the co_await expression\n  // on the corresponding awaitable.\n  //\n  // AWAITABLES must be awaitable types and must be movable (if passed as rvalue) or copyable\n  // (if passed as lvalue). The co_await expression will be executed on an rvalue of the\n  // copied awaitable.\n  template\u003ctypename... AWAITABLES\u003e\n  auto when_all_ready(AWAITABLES\u0026\u0026... awaitables)\n    -\u003e Awaitable\u003cstd::tuple\u003cdetail::when_all_task\u003ctypename awaitable_traits\u003cAWAITABLES\u003e::await_result_t\u003e...\u003e\u003e;\n\n  // Concurrently await each awaitable in a vector of input awaitables.\n  template\u003c\n    typename AWAITABLE,\n    typename RESULT = typename awaitable_traits\u003cAWAITABLE\u003e::await_result_t\u003e\n  auto when_all_ready(std::vector\u003cAWAITABLE\u003e awaitables)\n    -\u003e Awaitable\u003cstd::vector\u003cdetail::when_all_task\u003cRESULT\u003e\u003e\u003e;\n}\n```\n\nExample usage:\n```c++\ntask\u003cstd::string\u003e get_record(int id);\n\ntask\u003c\u003e example1()\n{\n  // Run 3 get_record() operations concurrently and wait until they're all ready.\n  // Returns a std::tuple of tasks that can be unpacked using structured bindings.\n  auto [task1, task2, task3] = co_await when_all_ready(\n    get_record(123),\n    get_record(456),\n    get_record(789));\n\n  // Unpack the result of each task\n  std::string\u0026 record1 = task1.result();\n  std::string\u0026 record2 = task2.result();\n  std::string\u0026 record3 = task3.result();\n\n  // Use records....\n}\n\ntask\u003c\u003e example2()\n{\n  // Create the input tasks. They don't start executing yet.\n  std::vector\u003ctask\u003cstd::string\u003e\u003e tasks;\n  for (int i = 0; i \u003c 1000; ++i)\n  {\n    tasks.emplace_back(get_record(i));\n  }\n\n  // Execute all tasks concurrently.\n  std::vector\u003cdetail::when_all_task\u003cstd::string\u003e\u003e resultTasks =\n    co_await when_all_ready(std::move(tasks));\n\n  // Unpack and handle each result individually once they're all complete.\n  for (int i = 0; i \u003c 1000; ++i)\n  {\n    try\n    {\n      std::string\u0026 record = tasks[i].result();\n      std::cout \u003c\u003c i \u003c\u003c \" = \" \u003c\u003c record \u003c\u003c std::endl;\n    }\n    catch (const std::exception\u0026 ex)\n    {\n      std::cout \u003c\u003c i \u003c\u003c \" : \" \u003c\u003c ex.what() \u003c\u003c std::endl;\n    }\n  }\n}\n```\n\n## `when_all()`\n\nThe `when_all()` function can be used to create a new Awaitable that when `co_await`ed\nwill `co_await` each of the input awaitables concurrently and return an aggregate of\ntheir individual results.\n\nWhen the returned awaitable is awaited, it will `co_await` each of the input awaitables\non the current thread. Once the first awaitable suspends, the second task will be started,\nand so on. The operations execute concurrently until they have all run to completion.\n\nOnce all component `co_await` operations have run to completion, an aggregate of the\nresults is constructed from each individual result. If an exception is thrown by any\nof the input tasks or if the construction of the aggregate result throws an exception\nthen the exception will propagate out of the `co_await` of the returned awaitable.\n\nIf multiple `co_await` operations fail with an exception then one of the exceptions\nwill propagate out of the `co_await when_all()` expression the other exceptions will be silently\nignored. It is not specified which operation's exception will be chosen.\n\nIf it is important to know which component `co_await` operation failed or to retain\nthe ability to obtain results of other operations even if some of them fail then you\nyou should use `when_all_ready()` instead.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/when_all.hpp\u003e\nnamespace cppcoro\n{\n  // Variadic version.\n  //\n  // Note that if the result of `co_await awaitable` yields a void-type\n  // for some awaitables then the corresponding component for that awaitable\n  // in the tuple will be an empty struct of type detail::void_value.\n  template\u003ctypename... AWAITABLES\u003e\n  auto when_all(AWAITABLES\u0026\u0026... awaitables)\n    -\u003e Awaitable\u003cstd::tuple\u003ctypename awaitable_traits\u003cAWAITABLES\u003e::await_result_t...\u003e\u003e;\n\n  // Overload for vector\u003cAwaitable\u003cvoid\u003e\u003e.\n  template\u003c\n    typename AWAITABLE,\n    typename RESULT = typename awaitable_traits\u003cAWAITABLE\u003e::await_result_t,\n    std::enable_if_t\u003cstd::is_void_v\u003cRESULT\u003e, int\u003e = 0\u003e\n  auto when_all(std::vector\u003cAWAITABLE\u003e awaitables)\n    -\u003e Awaitable\u003cvoid\u003e;\n\n  // Overload for vector\u003cAwaitable\u003cNonVoid\u003e\u003e that yield a value when awaited.\n  template\u003c\n    typename AWAITABLE,\n    typename RESULT = typename awaitable_traits\u003cAWAITABLE\u003e::await_result_t,\n    std::enable_if_t\u003c!std::is_void_v\u003cRESULT\u003e, int\u003e = 0\u003e\n  auto when_all(std::vector\u003cAWAITABLE\u003e awaitables)\n    -\u003e Awaitable\u003cstd::vector\u003cstd::conditional_t\u003c\n         std::is_lvalue_reference_v\u003cRESULT\u003e,\n         std::reference_wrapper\u003cstd::remove_reference_t\u003cRESULT\u003e\u003e,\n         std::remove_reference_t\u003cRESULT\u003e\u003e\u003e\u003e;\n}\n```\n\nExamples:\n```c++\ntask\u003cA\u003e get_a();\ntask\u003cB\u003e get_b();\n\ntask\u003c\u003e example1()\n{\n  // Run get_a() and get_b() concurrently.\n  // Task yields a std::tuple\u003cA, B\u003e which can be unpacked using structured bindings.\n  auto [a, b] = co_await when_all(get_a(), get_b());\n\n  // use a, b\n}\n\ntask\u003cstd::string\u003e get_record(int id);\n\ntask\u003c\u003e example2()\n{\n  std::vector\u003ctask\u003cstd::string\u003e\u003e tasks;\n  for (int i = 0; i \u003c 1000; ++i)\n  {\n    tasks.emplace_back(get_record(i));\n  }\n\n  // Concurrently execute all get_record() tasks.\n  // If any of them fail with an exception then the exception will propagate\n  // out of the co_await expression once they have all completed.\n  std::vector\u003cstd::string\u003e records = co_await when_all(std::move(tasks));\n\n  // Process results\n  for (int i = 0; i \u003c 1000; ++i)\n  {\n    std::cout \u003c\u003c i \u003c\u003c \" = \" \u003c\u003c records[i] \u003c\u003c std::endl;\n  }\n}\n```\n\n## `fmap()`\n\nThe `fmap()` function can be used to apply a callable function to the value(s) contained within\na container-type, returning a new container-type of the results of applying the function the\ncontained value(s).\n\nThe `fmap()` function can apply a function to values of type `generator\u003cT\u003e`, `recursive_generator\u003cT\u003e`\nand `async_generator\u003cT\u003e` as well as any value that supports the `Awaitable` concept (eg. `task\u003cT\u003e`).\n\nEach of these types provides an overload for `fmap()` that takes two arguments; a function to apply\nand the container value.\nSee documentation for each type for the supported `fmap()` overloads.\n\nFor example, the `fmap()` function can be used to apply a function to the eventual result of\na `task\u003cT\u003e`, producing a new `task\u003cU\u003e` that will complete with the return-value of the function.\n```c++\n// Given a function you want to apply that converts\n// a value of type A to value of type B.\nB a_to_b(A value);\n\n// And a task that yields a value of type A\ncppcoro::task\u003cA\u003e get_an_a();\n\n// We can apply the function to the result of the task using fmap()\n// and obtain a new task yielding the result.\ncppcoro::task\u003cB\u003e bTask = fmap(a_to_b, get_an_a());\n\n// An alternative syntax is to use the pipe notation.\ncppcoro::task\u003cB\u003e bTask = get_an_a() | cppcoro::fmap(a_to_b);\n```\n\nAPI Summary:\n```c++\n// \u003ccppcoro/fmap.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename FUNC\u003e\n  struct fmap_transform\n  {\n    fmap_transform(FUNC\u0026\u0026 func) noexcept(std::is_nothrow_move_constructible_v\u003cFUNC\u003e);\n    FUNC func;\n  };\n\n  // Type-deducing constructor for fmap_transform object that can be used\n  // in conjunction with operator|.\n  template\u003ctypename FUNC\u003e\n  fmap_transform\u003cFUNC\u003e fmap(FUNC\u0026\u0026 func);\n\n  // operator| overloads for providing pipe-based syntactic sugar for fmap()\n  // such that the expression:\n  //   \u003cvalue-expr\u003e | cppcoro::fmap(\u003cfunc-expr\u003e)\n  // is equivalent to:\n  //   fmap(\u003cfunc-expr\u003e, \u003cvalue-expr\u003e)\n\n  template\u003ctypename T, typename FUNC\u003e\n  decltype(auto) operator|(T\u0026\u0026 value, fmap_transform\u003cFUNC\u003e\u0026\u0026 transform);\n\n  template\u003ctypename T, typename FUNC\u003e\n  decltype(auto) operator|(T\u0026\u0026 value, fmap_transform\u003cFUNC\u003e\u0026 transform);\n\n  template\u003ctypename T, typename FUNC\u003e\n  decltype(auto) operator|(T\u0026\u0026 value, const fmap_transform\u003cFUNC\u003e\u0026 transform);\n\n  // Generic overload for all awaitable types.\n  //\n  // Returns an awaitable that when co_awaited, co_awaits the specified awaitable\n  // and applies the specified func to the result of the 'co_await awaitable'\n  // expression as if by 'std::invoke(func, co_await awaitable)'.\n  //\n  // If the type of 'co_await awaitable' expression is 'void' then co_awaiting the\n  // returned awaitable is equivalent to 'co_await awaitable, func()'.\n  template\u003c\n    typename FUNC,\n    typename AWAITABLE,\n    std::enable_if_t\u003cis_awaitable_v\u003cAWAITABLE\u003e, int\u003e = 0\u003e\n  auto fmap(FUNC\u0026\u0026 func, AWAITABLE\u0026\u0026 awaitable)\n    -\u003e Awaitable\u003cstd::invoke_result_t\u003cFUNC, typename awaitable_traits\u003cAWAITABLE\u003e::await_result_t\u003e\u003e;\n}\n```\n\nThe `fmap()` function is designed to look up the correct overload by argument-dependent\nlookup (ADL) so it should generally be called without the `cppcoro::` prefix.\n\n## `resume_on()`\n\nThe `resume_on()` function can be used to control the execution context that an awaitable\nwill resume the awaiting coroutine on when awaited. When applied to an `async_generator`\nit controls which execution context the `co_await g.begin()` and `co_await ++it` operations\nresume the awaiting coroutines on.\n\nNormally, the awaiting coroutine of an awaitable (eg. a `task`) or `async_generator` will\nresume execution on whatever thread the operation completed on. In some cases this may not\nbe the thread that you want to continue executing on. In these cases you can use the\n`resume_on()` function to create a new awaitable or generator that will resume execution\non a thread associated with a specified scheduler.\n\nThe `resume_on()` function can be used either as a normal function returning a new awaitable/generator.\nOr it can be used in a pipeline-syntax.\n\nExample:\n```c++\ntask\u003crecord\u003e load_record(int id);\n\nui_thread_scheduler uiThreadScheduler;\n\ntask\u003c\u003e example()\n{\n  // This will start load_record() on the current thread.\n  // Then when load_record() completes (probably on an I/O thread)\n  // it will reschedule execution onto thread pool and call to_json\n  // Once to_json completes it will transfer execution onto the\n  // ui thread before resuming this coroutine and returning the json text.\n  task\u003cstd::string\u003e jsonTask =\n    load_record(123)\n    | cppcoro::resume_on(threadpool::default())\n    | cppcoro::fmap(to_json)\n    | cppcoro::resume_on(uiThreadScheduler);\n\n  // At this point, all we've done is create a pipeline of tasks.\n  // The tasks haven't started executing yet.\n\n  // Await the result. Starts the pipeline of tasks.\n  std::string jsonText = co_await jsonTask;\n\n  // Guaranteed to be executing on ui thread here.\n\n  someUiControl.set_text(jsonText);\n}\n```\n\nAPI Summary:\n```c++\n// \u003ccppcoro/resume_on.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename SCHEDULER, typename AWAITABLE\u003e\n  auto resume_on(SCHEDULER\u0026 scheduler, AWAITABLE awaitable)\n    -\u003e Awaitable\u003ctypename awaitable_traits\u003cAWAITABLE\u003e::await_traits_t\u003e;\n\n  template\u003ctypename SCHEDULER, typename T\u003e\n  async_generator\u003cT\u003e resume_on(SCHEDULER\u0026 scheduler, async_generator\u003cT\u003e source);\n\n  template\u003ctypename SCHEDULER\u003e\n  struct resume_on_transform\n  {\n    explicit resume_on_transform(SCHEDULER\u0026 scheduler) noexcept;\n    SCHEDULER\u0026 scheduler;\n  };\n\n  // Construct a transform/operation that can be applied to a source object\n  // using \"pipe\" notation (ie. operator|).\n  template\u003ctypename SCHEDULER\u003e\n  resume_on_transform\u003cSCHEDULER\u003e resume_on(SCHEDULER\u0026 scheduler) noexcept;\n\n  // Equivalent to 'resume_on(transform.scheduler, std::forward\u003cT\u003e(value))'\n  template\u003ctypename T, typename SCHEDULER\u003e\n  decltype(auto) operator|(T\u0026\u0026 value, resume_on_transform\u003cSCHEDULER\u003e transform)\n  {\n    return resume_on(transform.scheduler, std::forward\u003cT\u003e(value));\n  }\n}\n```\n\n## `schedule_on()`\n\nThe `schedule_on()` function can be used to change the execution context that a given\nawaitable or `async_generator` starts executing on.\n\nWhen applied to an `async_generator` it also affects which execution context it resumes\non after `co_yield` statement.\n\nNote that the `schedule_on` transform does not specify the thread that the awaitable or\n`async_generator` will complete or yield results on, that is up to the implementation of\nthe awaitable or generator.\n\nSee the `resume_on()` operator for a transform that controls the thread the operation completes on.\n\nFor example:\n```c++\ntask\u003cint\u003e get_value();\nio_service ioSvc;\n\ntask\u003c\u003e example()\n{\n  // Starts executing get_value() on the current thread.\n  int a = co_await get_value();\n\n  // Starts executing get_value() on a thread associated with ioSvc.\n  int b = co_await schedule_on(ioSvc, get_value());\n}\n```\n\nAPI Summary:\n```c++\n// \u003ccppcoro/schedule_on.hpp\u003e\nnamespace cppcoro\n{\n  // Return a task that yields the same result as 't' but that\n  // ensures that 't' is co_await'ed on a thread associated with\n  // the specified scheduler. Resulting task will complete on\n  // whatever thread 't' would normally complete on.\n  template\u003ctypename SCHEDULER, typename AWAITABLE\u003e\n  auto schedule_on(SCHEDULER\u0026 scheduler, AWAITABLE awaitable)\n    -\u003e Awaitable\u003ctypename awaitable_traits\u003cAWAITABLE\u003e::await_result_t\u003e;\n\n  // Return a generator that yields the same sequence of results as\n  // 'source' but that ensures that execution of the coroutine starts\n  // execution on a thread associated with 'scheduler' and resumes\n  // after a 'co_yield' on a thread associated with 'scheduler'.\n  template\u003ctypename SCHEDULER, typename T\u003e\n  async_generator\u003cT\u003e schedule_on(SCHEDULER\u0026 scheduler, async_generator\u003cT\u003e source);\n\n  template\u003ctypename SCHEDULER\u003e\n  struct schedule_on_transform\n  {\n    explicit schedule_on_transform(SCHEDULER\u0026 scheduler) noexcept;\n    SCHEDULER\u0026 scheduler;\n  };\n\n  template\u003ctypename SCHEDULER\u003e\n  schedule_on_transform\u003cSCHEDULER\u003e schedule_on(SCHEDULER\u0026 scheduler) noexcept;\n\n  template\u003ctypename T, typename SCHEDULER\u003e\n  decltype(auto) operator|(T\u0026\u0026 value, schedule_on_transform\u003cSCHEDULER\u003e transform);\n}\n```\n\n# Metafunctions\n\n## `awaitable_traits\u003cT\u003e`\n\nThis template metafunction can be used to determine what the resulting type of a `co_await` expression\nwill be if applied to an expression of type `T`.\n\nNote that this assumes the value of type `T` is being awaited in a context where it is unaffected by\nany `await_transform` applied by the coroutine's promise object. The results may differ if a value\nof type `T` is awaited in such a context.\n\nThe `awaitable_traits\u003cT\u003e` template metafunction does not define the `awaiter_t` or `await_result_t`\nnested typedefs if type, `T`, is not awaitable. This allows its use in SFINAE contexts that disables\noverloads when `T` is not awaitable.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/awaitable_traits.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename T\u003e\n  struct awaitable_traits\n  {\n    // The type that results from applying `operator co_await()` to a value\n    // of type T, if T supports an `operator co_await()`, otherwise is type `T\u0026\u0026`.\n    typename awaiter_t = \u003cunspecified\u003e;\n\n    // The type of the result of co_await'ing a value of type T.\n    typename await_result_t = \u003cunspecified\u003e;\n  };\n}\n```\n\n## `is_awaitable\u003cT\u003e`\n\nThe `is_awaitable\u003cT\u003e` template metafunction allows you to query whether or not a given\ntype can be `co_await`ed or not from within a coroutine.\n\nAPI Summary:\n```c++\n// \u003ccppcoro/is_awaitable.hpp\u003e\nnamespace cppcoro\n{\n  template\u003ctypename T\u003e\n  struct is_awaitable : std::bool_constant\u003c...\u003e\n  {};\n\n  template\u003ctypename T\u003e\n  constexpr bool is_awaitable_v = is_awaitable\u003cT\u003e::value;\n}\n```\n\n# Concepts\n\n## `Awaitable\u003cT\u003e` concept\n\nAn `Awaitable\u003cT\u003e` is a concept that indicates that a type can be `co_await`ed in a coroutine context\nthat has no `await_transform` overloads and that the result of the `co_await` expression has type, `T`.\n\nFor example, the type `task\u003cT\u003e` implements the concept `Awaitable\u003cT\u0026\u0026\u003e` whereas the type `task\u003cT\u003e\u0026`\nimplements the concept `Awaitable\u003cT\u0026\u003e`.\n\n## `Awaiter\u003cT\u003e` concept\n\nAn `Awaiter\u003cT\u003e` is a concept that indicates a type contains the `await_ready`, `await_suspend` and\n`await_resume` methods required to implement the protocol for suspending/resuming an awaiting\ncoroutine.\n\nA type that satisfies `Awaiter\u003cT\u003e` must have, for an instance of the type, `awaiter`:\n- `awaiter.await_ready()` -\u003e `bool`\n- `awaiter.await_suspend(std::experimental::coroutine_handle\u003cvoid\u003e{})` -\u003e `void` or `bool` or `std::experimental::coroutine_handle\u003cP\u003e` for some `P`.\n- `awaiter.await_resume()` -\u003e `T`\n\nAny type that implements the `Awaiter\u003cT\u003e` concept also implements the `Awaitable\u003cT\u003e` concept.\n\n## `Scheduler` concept\n\nA `Scheduler` is a concept that allows scheduling execution of coroutines within some execution context.\n\n```c++\nconcept Scheduler\n{\n  Awaitable\u003cvoid\u003e schedule();\n}\n```\n\nGiven a type, `S`, that implements the `Scheduler` concept, and an instance, `s`, of type `S`:\n* The `s.schedule()` method returns an awaitable-type such that `co_await s.schedule()`\n  will unconditionally suspend the current coroutine and schedule it for resumption on the\n  execution context associated with the scheduler, `s`.\n* The result of the `co_await s.schedule()` expression has type `void`.\n\n```c++\ncppcoro::task\u003c\u003e f(Scheduler\u0026 scheduler)\n{\n  // Execution of the coroutine is initially on the caller's execution context.\n\n  // Suspends execution of the coroutine and schedules it for resumption on\n  // the scheduler's execution context.\n  co_await scheduler.schedule();\n\n  // At this point the coroutine is now executing on the scheduler's\n  // execution context.\n}\n```\n\n## `DelayedScheduler` concept\n\nA `DelayedScheduler` is a concept that allows a coroutine to schedule itself for execution on\nthe scheduler's execution context after a specified duration of time has elapsed.\n\n```c++\nconcept DelayedScheduler : Scheduler\n{\n  template\u003ctypename REP, typename RATIO\u003e\n  Awaitable\u003cvoid\u003e schedule_after(std::chrono::duration\u003cREP, RATIO\u003e delay);\n\n  template\u003ctypename REP, typename RATIO\u003e\n  Awaitable\u003cvoid\u003e schedule_after(\n    std::chrono::duration\u003cREP, RATIO\u003e delay,\n    cppcoro::cancellation_token cancellationToken);\n}\n```\n\nGiven a type, `S`, that implements the `DelayedScheduler` and an instance, `s` of type `S`:\n* The `s.schedule_after(delay)` method returns an object that can be awaited\n  such that `co_await s.schedule_after(delay)` suspends the current coroutine\n  for a duration of `delay` before scheduling the coroutine for resumption on\n  the execution context associated with the scheduler, `s`.\n* The `co_await s.schedule_after(delay)` expression has type `void`.\n\n# Building\n\nThe cppcoro library supports building under Windows with Visual Studio 2017 and Linux with Clang 5.0+.\n\nThis library makes use of the [Cake build system](https://github.com/lewissbaker/cake) (no, not the [C# one](http://cakebuild.net/)).\n\nThe cake build system is checked out automatically as a git submodule so you don't need to download or install it separately.\n\n## Building on Windows\n\nThis library currently requires Visual Studio 2017 or later and the Windows 10 SDK.\n\nSupport for Clang ([#3](https://github.com/lewissbaker/cppcoro/issues/3)) and Linux ([#15](https://github.com/lewissbaker/cppcoro/issues/15)) is planned.\n\n### Prerequisites\n\nThe Cake build-system is implemented in Python and requires Python 2.7 to be installed.\n\nEnsure Python 2.7 interpreter is in your PATH and available as 'python'.\n\nEnsure Visual Studio 2017 Update 3 or later is installed.\nNote that there are some known issues with coroutines in Update 2 or earlier that have been fixed in Update 3.\n\nYou can also use an experimental version of the Visual Studio compiler by downloading a NuGet package from https://vcppdogfooding.azurewebsites.net/ and unzipping the .nuget file to a directory.\nJust update the `config.cake` file to point at the unzipped location by modifying and uncommenting the following line:\n```python\nnugetPath = None # r'C:\\Path\\To\\VisualCppTools.14.0.25224-Pre'\n```\n\nEnsure that you have the Windows 10 SDK installed.\nIt will use the latest Windows 10 SDK and Universal C Runtime version by default.\n\n### Cloning the repository\n\nThe cppcoro repository makes use of git submodules to pull in the source for the Cake build system.\n\nThis means you need to pass the `--recursive` flag to the `git clone` command. eg.\n```\nc:\\Code\u003e git clone --recursive https://github.com/lewissbaker/cppcoro.git\n```\n\nIf you have already cloned cppcoro, then you should update the submodules after pulling changes.\n```\nc:\\Code\\cppcoro\u003e git submodule update --init --recursive\n```\n\n### Building from the command-line\n\nTo build from the command-line just run 'cake.bat' in the workspace root.\n\neg.\n```\nC:\\cppcoro\u003e cake.bat\nBuilding with C:\\cppcoro\\config.cake - Variant(release='debug', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')\nBuilding with C:\\cppcoro\\config.cake - Variant(release='optimised', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')\nBuilding with C:\\cppcoro\\config.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')\nBuilding with C:\\cppcoro\\config.cake - Variant(release='optimised', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')\nCompiling test\\main.cpp\nCompiling test\\main.cpp\nCompiling test\\main.cpp\nCompiling test\\main.cpp\n...\nLinking build\\windows_x86_msvc14.10_debug\\test\\run.exe\nLinking build\\windows_x64_msvc14.10_optimised\\test\\run.exe\nLinking build\\windows_x86_msvc14.10_optimised\\test\\run.exe\nLinking build\\windows_x64_msvc14.10_debug\\test\\run.exe\nGenerating code\nFinished generating code\nGenerating code\nFinished generating code\nBuild succeeded.\nBuild took 0:00:02.419.\n```\n\nBy default, running `cake` with no arguments will build all projects with all build variants and execute the unit-tests.\nYou can narrow what is built by passing additional command-line arguments.\neg.\n```\nc:\\cppcoro\u003e cake.bat release=debug architecture=x64 lib/build.cake\nBuilding with C:\\Users\\Lewis\\Code\\cppcoro\\config.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')\nArchiving build\\windows_x64_msvc14.10_debug\\lib\\cppcoro.lib\nBuild succeeded.\nBuild took 0:00:00.321.\n```\n\nYou can run `cake --help` to list available command-line options.\n\n### Building Visual Studio project files\n\nTo develop from within Visual Studio you can build .vcproj/.sln files by running `cake.bat -p`.\n\neg.\n```\nc:\\cppcoro\u003e cake.bat -p\nBuilding with C:\\cppcoro\\config.cake - Variant(release='debug', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')\nBuilding with C:\\cppcoro\\config.cake - Variant(release='optimised', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')\nBuilding with C:\\cppcoro\\config.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')\nBuilding with C:\\cppcoro\\config.cake - Variant(release='optimised', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')\nGenerating Solution build/project/cppcoro.sln\nGenerating Project build/project/cppcoro_tests.vcxproj\nGenerating Filters build/project/cppcoro_tests.vcxproj.filters\nGenerating Project build/project/cppcoro.vcxproj\nGenerating Filters build/project/cppcoro.vcxproj.filters\nBuild succeeded.\nBuild took 0:00:00.247.\n```\n\nWhen you build these projects from within Visual Studio it will call out to cake to perform the compilation.\n\n## Building on Linux\n\nThe cppcoro project can also be built under Linux using Clang + libc++ 5.0 or later.\n\nBuilding cppcoro has been tested under Ubuntu 17.04.\n\n### Prerequisities\n\nEnsure you have the following packages installed:\n* Python 2.7\n* Clang \u003e= 5.0\n* LLD \u003e= 5.0\n* libc++ \u003e= 5.0\n\n\n### Building cppcoro\n\nThis is assuming you have Clang and libc++ built and installed.\n\nIf you don't have Clang configured yet, see the following sections\nfor details on setting up Clang for building with cppcoro.\n\nCheckout cppcoro and its submodules:\n```\ngit clone --recursive https://github.com/lewissbaker/cppcoro.git cppcoro\n```\n\nRun `init.sh` to setup the `cake` bash function:\n```\ncd cppcoro\nsource init.sh\n```\n\nThen you can run `cake` from the workspace root to build cppcoro and run tests:\n```\n$ cake\n```\n\nYou can specify additional command-line arguments to customise the build:\n* `--help` will print out help for command-line arguments\n* `--debug=run` will show the build command-lines being run\n* `release=debug` or `release=optimised` will limit the build variant to\n   either debug or optimised (by default it will build both).\n* `lib/build.cake` will just build the cppcoro library and not the tests.\n* `test/build.cake@task_tests.cpp` will just compile a particular source file\n* `test/build.cake@testresult` will build and run the tests\n\nFor example:\n```\n$ cake --debug=run release=debug lib/build.cake\n```\n\n### Customising location of Clang\n\nIf your clang compiler is not located at `/usr/bin/clang` then you can specify an\nalternative location using one or more of the following command-line options for `cake`:\n\n* `--clang-executable=\u003cname\u003e` - Specify the clang executable name to use instead of `clang`.\n  eg. to force use of Clang 8.0 pass `--clang-executable=clang-8`\n* `--clang-executable=\u003cabspath\u003e` - Specify the full path to clang executable.\n  The build system will also look for other executables in the same directory.\n  If this path has the form `\u003cprefix\u003e/bin/\u003cname\u003e` then this will also set the default clang-install-prefix to `\u003cprefix\u003e`.\n* `--clang-install-prefix=\u003cpath\u003e` - Specify path where clang has been installed.\n  This will cause the build system to look for clang under `\u003cpath\u003e/bin` (unless overridden by `--clang-executable`).\n* `--libcxx-install-prefix=\u003cpath\u003e` - Specify path where libc++ has been installed.\n  By default the build system will look for libc++ in the same location as clang.\n  Use this command-line option if it is installed in a different location.\n\nExample: Use a specific version of clang installed in the default location\n```\n$ cake --clang-executable=clang-8\n```\n\nExample: Use the default version of clang from a custom location\n```\n$ cake --clang-install-prefix=/path/to/clang-install\n```\n\nExample: Use a specific version of clang, in a custom location, with libc++ from a different location\n```\n$ cake --clang-executable=/path/to/clang-install/bin/clang-8 --libcxx-install-prefix=/path/to/libcxx-install\n```\n\n### Using a snapshot build of Clang\n\nIf your Linux distribution does not have a version of Clang 5.0 or later\navailable, you can install a snapshot build from the LLVM project.\n\nFollow instructions at http://apt.llvm.org/ to setup your package manager\nto support pulling from the LLVM package manager.\n\nFor example, for Ubuntu 17.04 Zesty:\n\nEdit `/etc/apt/sources.list` and add the following lines:\n```\ndeb http://apt.llvm.org/zesty/ llvm-toolchain-zesty main\ndeb-src http://apt.llvm.org/zesty/ llvm-toolchain-zesty main\n```\n\nInstall the PGP key for those packages:\n```\n$ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n```\n\nInstall Clang and LLD:\n```\n$ sudo apt-get install clang-6.0 lld-6.0\n```\n\nThe LLVM snapshot builds do not include libc++ versions so you'll need to build that yourself.\nSee below.\n\n### Building your own Clang\n\nYou can also use the bleeding-edge Clang version by building Clang from source yourself.\n\nSee instructions here:\n\nTo do this you will need to install the following pre-requisites:\n```\n$ sudo apt-get install git cmake ninja-build clang lld\n```\n\nNote that we are using your distribution's version of clang to build\nclang from source. GCC could also be used here instead.\n\n\nCheckout LLVM + Clang + LLD + libc++ repositories:\n```\nmkdir llvm\ncd llvm\ngit clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm\ngit clone --depth=1 https://github.com/llvm-mirror/clang.git llvm/tools/clang\ngit clone --depth=1 https://github.com/llvm-mirror/lld.git llvm/tools/lld\ngit clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm/projects/libcxx\nln -s llvm/tools/clang clang\nln -s llvm/tools/lld lld\nln -s llvm/projects/libcxx libcxx\n```\n\nConfigure and build Clang:\n```\nmkdir clang-build\ncd clang-build\ncmake -GNinja \\\n      -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \\\n      -DCMAKE_C_COMPILER=/usr/bin/clang \\\n      -DCMAKE_BUILD_TYPE=MinSizeRel \\\n      -DCMAKE_INSTALL_PREFIX=\"/path/to/clang/install\"\n      -DCMAKE_BUILD_WITH_INSTALL_RPATH=\"yes\" \\\n      -DLLVM_TARGETS_TO_BUILD=X86 \\\n      -DLLVM_ENABLE_PROJECTS=\"lld;clang\" \\\n      ../llvm\nninja install-clang \\\n      install-clang-headers \\\n      install-llvm-ar \\\n      install-lld\n```\n\n### Building libc++\n\nThe cppcoro project requires libc++ as it contains the `\u003cexperimental/coroutine\u003e`\nheader required to use C++ coroutines under Clang.\n\nCheckout `libc++` + `llvm`:\n```\nmkdir llvm\ncd llvm\ngit clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm\ngit clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm/projects/libcxx\nln -s llvm/projects/libcxx libcxx\n```\n\nBuild `libc++`:\n```\nmkdir libcxx-build\ncd libcxx-build\ncmake -GNinja \\\n      -DCMAKE_CXX_COMPILER=\"/path/to/clang/install/bin/clang++\" \\\n      -DCMAKE_C_COMPILER=\"/path/to/clang/install/bin/clang\" \\\n      -DCMAKE_BUILD_TYPE=Release \\\n      -DCMAKE_INSTALL_PREFIX=\"/path/to/clang/install\" \\\n      -DLLVM_PATH=\"../llvm\" \\\n      -DLIBCXX_CXX_ABI=libstdc++ \\\n      -DLIBCXX_CXX_ABI_INCLUDE_PATHS=\"/usr/include/c++/6.3.0/;/usr/include/x86_64-linux-gnu/c++/6.3.0/\" \\\n      ../libcxx\nninja cxx\nninja install\n```\n\nThis will build and install libc++ into the same install directory where you have clang installed.\n\n## Installing from vcpkg\n\nThe cppcoro port in vcpkg is kept up to date by Microsoft team members and community contributors. The url of vcpkg is: https://github.com/Microsoft/vcpkg . You can download and install cppcoro using the vcpkg dependency manager:\n\n```shell\ngit clone https://github.com/Microsoft/vcpkg.git\ncd vcpkg\n./bootstrap-vcpkg.sh  # ./bootstrap-vcpkg.bat for Windows\n./vcpkg integrate install\n./vcpkg install cppcoro\n```\n\nIf the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.\n\n# Support\n\nGitHub issues are the primary mechanism for support, bug reports and feature requests.\n\nContributions are welcome and pull-requests will be happily reviewed.\nI only ask that you agree to license any contributions that you make under the MIT license.\n\nIf you have general questions about C++ coroutines, you can generally find someone to help\nin the `#coroutines` channel on [Cpplang Slack](https://cpplang.slack.com/) group.\n","funding_links":[],"categories":["C++","TODO scan for Android support in followings","Concurrency \u0026 Parallelism","Software"],"sub_categories":["UI Test Automation Scripting"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flewissbaker%2Fcppcoro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flewissbaker%2Fcppcoro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flewissbaker%2Fcppcoro/lists"}