### Start Task with Initialization Callback Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Demonstrates starting a task within a nursery and providing a callback that is invoked after the task's initialization is complete. This allows for synchronization between the parent and the child task during setup. ```cpp CORRAL_WITH_NURSERY(n) { co_await n.start([&](corral::TaskStarted<> started) -> corral::Task<> { // ...initialize... started(); // ...work... }); // ...communicate with the task... }; ``` -------------------------------- ### Corral Testing Setup with Catch2 Source: https://github.com/hudson-trading/corral/blob/master/CMakeLists.txt Configures the build to include testing, allowing for the use of Catch2 for unit tests. It supports fetching Catch2 from a Git repository or using an installed version. ```cmake if(BUILD_TESTING) set(CORRAL_TEST_RUNNER "" CACHE STRING "Use this wrapper for running tests" ) set(CORRAL_CATCH2 "https://github.com/catchorg/catch2" CACHE STRING "include path for Catch2 (or Git repository URL)" ) if("${CORRAL_CATCH2}" MATCHES ".*://.*")) message( STATUS "Trying to bundle Catch2 ver 2.xx.x (maybe via git cloning whole Boost)." ) include(FetchContent) FetchContent_Declare( catch GIT_REPOSITORY "${CORRAL_CATCH2}" GIT_TAG v2.13.9 ) FetchContent_MakeAvailable(catch) else() message(STATUS "Trying to use installed Catch2.") set(Catch_INCLUDE_DIR "${CORRAL_CATCH2}") find_package(Catch2 2.13.9 REQUIRED) endif() enable_testing() add_executable(corral_basic_test test/basic_test.cc test/helpers.h) target_link_libraries(corral_basic_test PRIVATE corral Catch2::Catch2) target_compile_definitions(corral_basic_test PRIVATE CATCH_CONFIG_MAIN) add_test(NAME corral_basic_test COMMAND ${CORRAL_TEST_RUNNER} corral_basic_test ) # # ASIO interaction # set(CORRAL_BOOST "" CACHE STRING "include path for boost (or Git repository URL)" ) if(NOT ("${CORRAL_BOOST}" STREQUAL "")) if("${CORRAL_BOOST}" MATCHES ".*://.*")) message( STATUS "Trying to bundle Boost (maybe via git cloning whole Boost)." ) FetchContent_Declare( Boost GIT_REPOSITORY "${CORRAL_BOOST}" GIT_TAG boost-1.80.0 ) FetchContent_MakeAvailable(Boost) set(CORRAL_BOOST_ASIO_LIB_NAME "Boost::asio") else() message(STATUS "Trying to use installed Boost.") set(Boost_INCLUDE_DIR "${CORRAL_BOOST}") find_package(Boost 1.77.0 REQUIRED) set(CORRAL_BOOST_ASIO_LIB_NAME "Boost::boost") endif() add_executable(corral_asio_test test/asio_test.cc test/helpers.h) target_link_libraries(corral_asio_test PRIVATE corral Catch2::Catch2) target_compile_definitions(corral_asio_test PRIVATE CATCH_CONFIG_MAIN) if("${CORRAL_TEST_RUNNER}" STREQUAL "")) message(STATUS "Trying to use installed OpenSSL.") find_package(OpenSSL) if(OpenSSL_FOUND) message(STATUS "Using installed OpenSSL.") target_compile_definitions(corral_asio_test PRIVATE CORRAL_HAVE_OPENSSL) target_link_libraries( corral_asio_test PRIVATE OpenSSL::SSL OpenSSL::Crypto ) else() message(STATUS "Failed to find installed OpenSSL") endif() endif() add_test(NAME corral_asio_test COMMAND ${CORRAL_TEST_RUNNER} corral_asio_test ) endif() endif() ``` -------------------------------- ### Corral Installation Instructions Source: https://github.com/hudson-trading/corral/blob/master/CMakeLists.txt Specifies how the Corral library and its targets should be installed. It ensures that the library headers and targets are correctly placed for external use. ```cmake # # Installation # install(DIRECTORY corral DESTINATION include) install( TARGETS corral EXPORT corralTargets INCLUDES DESTINATION include ) ``` -------------------------------- ### Corral Examples Build Configuration Source: https://github.com/hudson-trading/corral/blob/master/CMakeLists.txt Controls whether the examples for the Corral project are built. This is a boolean cache variable that can be set to ON or OFF. ```cmake # # Examples # set(CORRAL_EXAMPLES OFF CACHE BOOL "Build examples" ) if(CORRAL_EXAMPLES) add_subdirectory(examples) endif() ``` -------------------------------- ### Opening an AsyncFD Task Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md A simple example of creating a task that opens an AsyncFD, demonstrating how to return an awaitable from a function. ```cpp corral::Task openAsyncFD() { co_return co_await AsyncFD::open("..."); } ``` -------------------------------- ### Start Task with Initialization and Result Communication Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Shows how to start a task that communicates a value back to its spawner upon initialization. The `TaskStarted` parameter can accept a value, which then becomes the result of the `co_await n.start()` expression. ```cpp CORRAL_WITH_NURSERY(n) { int v = co_await n.start([&](corral::TaskStarted started) -> corral::Task { started(42); // ...further work... }); assert(v == 42); }; ``` -------------------------------- ### Corral Task Hierarchy Example Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Demonstrates the creation of a task hierarchy using Corral's nursery and allOf features. It starts child tasks for MyChildLiveClass1 and MyChildLiveClass2, suspends indefinitely after completion, and establishes a task structure mirroring the object hierarchy. ```C++ class MyParentLiveClass { MyChildLiveClass1 child1_; MyChildLiveClass2 child2_; public: corral::Task run(corral::TaskStarted<> started = {}) { CORRAL_WITH_NURSERY(n) { co_await corral::allOf( n.start(&MyChildLiveClass1::run, &child1_), n.start(&MyChildLiveClass2::run, &child2_)); started(); CORRAL_SUSPEND_FOREVER(); }; } }; ``` -------------------------------- ### Work with AsyncFD Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Provides an example of opening an asynchronous file descriptor and then consuming data from it. This function illustrates the basic workflow of using AsyncFD within the Corral framework. ```C++ corral::Task workWithAsyncFD() { AsyncFD fd = co_await openAsyncFD(); co_await consumeAsyncFD(std::move(fd)); } ``` -------------------------------- ### Corral Nursery API Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Provides documentation for core Corral Nursery functionalities, including starting tasks and managing nursery lifecycles. ```APIDOC Nursery: start(corral::Task task, Args&&... args) Starts a task within the nursery. Parameters: task: The task to start. args: Arguments to pass to the task. Returns: A handle to the started task. openNursery(corral::Nursery*& nursery_ptr, corral::TaskStarted<> started = {}) -> corral::Task Opens a nursery and manages its lifecycle. Parameters: nursery_ptr: A reference to a pointer that will hold the nursery. started: A callback to be invoked when the nursery is started. Returns: A task representing the nursery's lifecycle. CORRAL_WITH_NURSERY(nursery_variable): A macro for creating a nursery scope. Parameters: nursery_variable: The name of the variable to hold the nursery. Usage: CORRAL_WITH_NURSERY(n) { // code within the nursery scope }; CORRAL_SUSPEND_FOREVER(): Suspends the current task indefinitely. corral::cancel: A special return value to signal cancellation. corral::noncancellable(): Ensures a block of code is not cancellable. corral::untilCancelledAnd(corral::Task task): Runs a task until cancellation and then performs additional cleanup. ``` -------------------------------- ### Using a Live Object within a Nursery Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Illustrates how to use a live object by starting its `run()` method in a nursery. This ensures that the object's background tasks are managed correctly and that the object is usable within the nursery's scope. ```cpp MyLiveClass obj; CORRAL_WITH_NURSERY(n) { co_await n.start(&MyLiveClass::run, &obj); // Now the object can be used in the remainder // of the nursery block obj.beginThing(); co_return corral::cancel; // shut down the background task // and any children thereof }; ``` -------------------------------- ### Running Corral with Qt Event Loop Source: https://github.com/hudson-trading/corral/blob/master/doc/02_adapting.md This C++ code snippet shows how to integrate Corral's asynchronous task execution with Qt's event loop. It defines an example asynchronous task `asyncMain` and uses `corral::run` in the `main` function, passing the `QApplication` instance and the task to be executed. ```cpp corral::Task asyncMain() { // spawn more tasks // wait on Qt signals // do awesome things } int main(int argc, char** argv) { QApplication app(argc, argv); return corral::run(app, asyncMain()); } ``` -------------------------------- ### Corral EventLoopTraits Specialization for QApplication Source: https://github.com/hudson-trading/corral/blob/master/doc/02_adapting.md This C++ code demonstrates how to specialize the `corral::EventLoopTraits` for Qt's `QApplication`. It defines static methods to run, stop, check the running status, and get a unique ID for the event loop, enabling Corral to manage Qt's event loop for asynchronous operations. ```cpp namespace corral { template<> struct EventLoopTraits { static void run(QApplication& app) { app.exec(); } static void stop(QApplication& app) { app.exit(); } static bool isRunning(QApplication& app) noexcept { return false; } static EventLoopID eventLoopID(QApplication& app) { return EventLoopID(&app); } }; } ``` -------------------------------- ### Corral Nursery with CORRAL_WITH_NURSERY Macro Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Illustrates the use of the `CORRAL_WITH_NURSERY` macro to manage a dynamic set of child tasks. The macro provides a `corral::Nursery` reference to start new tasks within the nursery's scope. ```cpp corral::Task foo() { CORRAL_WITH_NURSERY(n) { n.start(async_hello); n.start(wrap_hello); co_return corral::join; }; } ``` ```cpp corral::Task serve(tcp::socket&); corral::Task acceptorLoop(tcp::acceptor& acc) { CORRAL_WITH_NURSERY(n) { while (true) { tcp::socket sock = co_await acc.async_accept(io_context, corral::asio_awaitable); n.start(serve, std::move(sock)); } }; } ``` ```cpp corral::Task foo(int&); corral::Task bar() { int x = 0; CORRAL_WITH_NURSERY(n) { for (int i = 0; i != 10; ++i) { n.start(foo, std::ref(x)); } co_return corral::join; }; } ``` ```cpp corral::Task foo(int&); corral::Task bar() { int x = 0; CORRAL_WITH_NURSERY(n) { for (int i = 0; i != 10; ++i) { n.start(foo, std::ref(x)); } co_return corral::join; }; } ``` ```cpp corral::Event started; CORRAL_WITH_NURSERY(n) { n.start([&]() -> corral::Task<> { // ...initialize... started.trigger(); // ...work... }); co_await started; ``` -------------------------------- ### CORRAL_WITH_NURSERY Example Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Demonstrates a lifetime trap when a nursery is still alive after the object it's associated with is destroyed. This highlights the need for careful lifecycle management. ```cpp CORRAL_WITH_NURSERY(n) { MyLiveClass obj(n); obj.doSomething(); co_return corral::cancel; // at this point `obj` is destroyed (because it is local to this // block), but `n` is still alive, and background tasks of `obj` // spawned there are still running, potentially holding dangling // references to `obj` } ``` -------------------------------- ### Nursery Class Definition Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Defines the Nursery class, responsible for managing concurrent tasks. It includes methods for starting tasks, canceling them, and querying their status. ```cpp class Nursery { private: Nursery(); Nursery(Nursery&&); public: template requires(Awaitable>) void start(Callable, Args...); /*1*/ template requires(Awaitable>>) Awaitable auto start(Callable, Args...); /*2*/ void cancel(); size_t taskCount() const noexcept; Executor* executor() const noexcept; }; Task openNursery(Nursery*&); ``` -------------------------------- ### Reading Data with QTcpSocket using Coroutines Source: https://github.com/hudson-trading/corral/blob/master/doc/02_adapting.md An example of a Corral task that reads data from a `QTcpSocket` asynchronously. It uses the `QtSignalAwaitable` to pause execution until data is available (`readyRead` signal) and handles reading, EOF, and errors. ```cpp corral::Task my_read_all(QTcpSocket& sock) { std::array buf; while (true) { size_t avail; while ((avail = sock.bytesAvailable()) == 0) co_await QtSignalAwaitable(&sock, &QTcpSocket::readyRead); ssize_t len = sock.read(buf.data(), min(buf.size(), avail)); if (len > 0) { /* use buf[0..len] somehow */ } else if (len == 0) co_return; // EOF else throw std::runtime_error(sock.errorString().toStdString()); } } ``` -------------------------------- ### Corral anyOf Combiner Usage Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Demonstrates the use of `corral::anyOf` to run multiple awaitables concurrently, returning the result of the first one to complete and cancelling the rest. Includes examples with timeouts and external cancellation events. ```cpp co_await corral::anyOf(somethingLong(), corral::sleepFor(io_service, 3s)); ``` ```cpp corral::Event cancelEvent; co_await corral::anyOf(somethingLong(), cancelEvent); // later, elsewhere: cancelEvent.set(); ``` -------------------------------- ### C++ Reader with Regular Nursery Source: https://github.com/hudson-trading/corral/blob/master/doc/04_callback_bridging.md Implements the IReader interface using a regular Corral nursery to manage asynchronous read operations. It starts a task within the nursery to perform the read and invoke the callback. ```cpp class MyReader : public IReader { corral::Nursery* nursery_ = nullptr; // complicated logic goes here: corral::Task readImpl(void* buf, size_t len); public: corral::Task run() { return corral::openNursery(nursery_); } void read(void* buf, size_t len, std::function cb) { nursery_->start([=]() -> corral::Task<> { try { ssize_t result = co_await readImpl(buf, len); cb(result); } catch (std::exception&) { cb(-1); } }); } }; ``` -------------------------------- ### Spawn Task with Delayed Execution in Corral Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Demonstrates spawning a task that waits for a specified duration before executing a callback. This function takes a nursery reference to start the task. ```cpp void delayedPrint(corral::Nursery& n) { n.start([]() -> corral::Task<> { co_await corral::sleepFor(io_service, 1s); std::cout << "Hello, world!" << std::endl; }); } ``` -------------------------------- ### C++ ThreadPool with Cancellation Token Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Illustrates how to use a cancellation token with corral::ThreadPool::run to allow CPU-bound tasks to be interrupted gracefully. The example shows a SHA256 hashing function that periodically checks the cancellation token. ```cpp SHA256 hash = co_await threadPool.run( [&](corral::ThreadPool::CancelToken cancelled) { SHA256_CTX ctx; const char* begin = data.data(); size_t size = hdr.DataSize; SHA256_Init(&ctx); while (size != 0 && !cancelled) { size_t chunkSize = std::min(size, 4096); SHA256_Update(&ctx, begin, chunkSize); begin += chunkSize, size -= chunkSize; } SHA256 out; SHA256_Final(&ctx, out.data); return out; }); ``` -------------------------------- ### Event Class Methods Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Details the methods of the `Event` class for managing and querying the event state. `trigger()` activates the event, `triggered()` checks if it has been activated, and `operator co_await()` or `get()` allows tasks to wait for the event. ```cpp void Event::trigger(); Awaitable auto Event::operator co_await(); bool Event::triggered() const noexcept; bool Event::get() const noexcept; Awaitable auto Event::get(); ``` -------------------------------- ### Using CBPortal to Await Callback Invocations Source: https://github.com/hudson-trading/corral/blob/master/doc/04_callback_bridging.md Demonstrates how to wrap a callback-style listener into an awaitable object using corral::CBPortal. It shows how to set up the portal, wait for the first callback, and then continuously serve new connections as they arrive. ```cpp corral::Task listen(int port) { CORRAL_WITH_NURSERY(n) { corral::CBPortal cbp; // callback takes (int) std::optional listener; // Set up the portal and wait for the first CB invocation int fd = co_await corral::untilCBCalled([&](auto cb) { listener = doListen(port, cb); }, cbp); while (true) { n.start(serve(fd)); // Wait for another invocation fd = co_await cbp; } }; } ``` -------------------------------- ### MyLiveClass Live Object Implementation Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Shows a typical implementation of a 'live object' using a nursery pointer. The `run()` method initializes the nursery and stashes a pointer for other methods to use for spawning tasks. ```cpp class MyLiveClass { corral::Nursery* nursery_ = nullptr; public: corral::Task run(corral::TaskStarted<> started = {}) { CORRAL_WITH_NURSERY(n) { auto guard = folly::makeGuard([this] { nursery_ = nullptr; }); nursery_ = &n; started(); CORRAL_SUSPEND_FOREVER(); }; } // Now `nursery_` can be used to spawn tasks corral::Task asyncThing(); void beginThing() { nursery_->start(&MyLiveClass::asyncThing, this); } }; ``` -------------------------------- ### Simplified run() with corral::openNursery Source: https://github.com/hudson-trading/corral/blob/master/doc/03_live_objects.md Demonstrates a more efficient way to wrap an async function using `corral::openNursery()`. This function opens a nursery, manages its lifecycle, and clears the nursery pointer upon closure. ```cpp corral::Task run(corral::TaskStarted<> started = {}) { return corral::openNursery(nursery_, std::move(started)); } ``` -------------------------------- ### Value Class Definition Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Defines the Value template class, including its constructors, methods for getting, setting, and modifying the value, and various awaitable functions for conditional waiting. ```cpp template class Value { Value(Value&&) = delete; Value& operator=(Value&&) = delete; class Comparison; public: Value(); explicit Value(T value); const T& get() const noexcept; operator const T&() const noexcept; void set(T value); Value& operator=(T value); T modify(std::invocable auto&& fn); Awaitable auto untilMatches(std::invocable auto&& pred); Awaitable auto untilEquals(T expected); Awaitable> auto untilChanged(std::invocable auto&& pred); Awaitable> auto untilChanged(T from, T to); Awaitable> auto untilChanged(); T operator++(); T operator++(int); T operator+=(auto&&); // ...etc... Comparison operator==(auto&&); bool operator==(auto&&) const; // ...ditto for other comparisions... auto operator<=>(auto&&) const; }; template class Value::Comparison { public: operator bool() const; friend Awaitable auto until(Comparision&&); }; ``` -------------------------------- ### Qt6 Integration for Echo Server Source: https://github.com/hudson-trading/corral/blob/master/examples/CMakeLists.txt This CMake snippet finds the Qt6 package and, if found, adds a Qt6-based echo server executable. It links the executable against the 'corral' library and necessary Qt6 components (Core and Network). ```cmake find_package(Qt6 COMPONENTS Core Network) if(${Qt6Core_FOUND}) qt_add_executable(qt_echo_server qt_echo_server.cc) target_link_libraries(qt_echo_server PRIVATE corral Qt6::Core Qt6::Network) endif() ``` -------------------------------- ### Corral Core Functionality Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Includes the main header for corral functionality. For Boost.Asio integration, an additional header is required. This section outlines the entry point for running tasks and the definitions of core concepts and types. ```cpp #include #include ``` -------------------------------- ### Corral Library Build Configuration Source: https://github.com/hudson-trading/corral/blob/master/CMakeLists.txt Configures the C++ standard, project name, and defines the Corral library as an interface library. It specifies the source files to be included in the library and sets up include directories. ```cmake cmake_minimum_required(VERSION 3.15) project(corral) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_library( corral INTERFACE ) target_sources(corral INTERFACE corral/asio.h corral/CBPortal.h corral/Channel.h corral/concepts.h corral/config.h corral/defs.h corral/Event.h corral/Executor.h corral/corral.h corral/Nursery.h corral/ParkingLot.h corral/run.h corral/Semaphore.h corral/Shared.h corral/Task.h corral/ThreadPool.h corral/utility.h corral/Value.h corral/wait.h corral/detail/ABI.h corral/detail/exception.h corral/detail/frames.h corral/detail/introspect.h corral/detail/IntrusiveList.h corral/detail/IntrusivePtr.h corral/detail/ParkingLot.h corral/detail/platform.h corral/detail/PointerBits.h corral/detail/Promise.h corral/detail/Queue.h corral/detail/ScopeGuard.h corral/detail/TaskAwaiter.h corral/detail/utility.h corral/detail/wait.h ) target_include_directories(corral INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) ``` -------------------------------- ### Add Executables and Link Libraries Source: https://github.com/hudson-trading/corral/blob/master/examples/CMakeLists.txt CMake code to add several Boost.Asio-based executables to the build. It conditionally adds the 'asio_netcat' executable based on the compiler and system. All executables are linked against the 'corral' library and a Boost.Asio library. ```cmake if(NOT CXX_FLAGS_NO_EXCEPTIONS AND NOT CXX_FLAGS_NO_RTTI) add_executable(asio_hello_world asio_hello_world.cc) target_link_libraries( asio_hello_world PRIVATE corral "${CORRAL_BOOST_ASIO_LIB_NAME}" ) add_executable(asio_echo_server asio_echo_server.cc) target_link_libraries( asio_echo_server PRIVATE corral "${CORRAL_BOOST_ASIO_LIB_NAME}" ) add_executable(asio_http_downloader asio_http_downloader.cc) target_link_libraries( asio_http_downloader PRIVATE corral "${CORRAL_BOOST_ASIO_LIB_NAME}" ) add_executable(asio_happy_eyeballs asio_happy_eyeballs.cc) target_link_libraries( asio_happy_eyeballs PRIVATE corral "${CORRAL_BOOST_ASIO_LIB_NAME}" ) if(NOT ((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_SYSTEM_NAME STREQUAL "Windows")) ) add_executable(asio_netcat asio_netcat.cc) target_link_libraries( asio_netcat PRIVATE corral "${CORRAL_BOOST_ASIO_LIB_NAME}" ) endif() endif() ``` -------------------------------- ### EventLoopTraits Methods Documentation Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Provides detailed explanations for each static method within EventLoopTraits, including their purpose, parameters, return values, and usage considerations. ```APIDOC EventLoopTraits::run(T&) - Runs the event loop. Should not return until stop() is called. EventLoopTraits::stop(T&) - Requests the event loop to stop; run() should return soon. EventLoopTraits::isRunning(T&) const noexcept - Returns true if the event loop is currently running. Only used to guard against re-entering the same event loop; if infeasible to implement, it's OK to always return false. EventLoopTraits::eventLoopID(T& eventLoop) - Returns a unique identifier for the event loop that eventLoop wraps. For the vast majority of use cases, return EventLoopID(&eventLoop) will suffice. However, if one event loop is running on top of another (through any sort of "guest mode"), or if the same event loop might be accessed through multiple objects, this should be implemented to look inside that abstraction and return the ID of the topmost/"host" event loop. ``` -------------------------------- ### Simplifying Movable Awaitables with corral::makeAwaitable Source: https://github.com/hudson-trading/corral/blob/master/doc/02_adapting.md Shows how to use the `corral::makeAwaitable` utility function to automatically generate the operation description type. This simplifies the process when the description class only forwards arguments to the operation state constructor. ```cpp class AsyncRead { /* same as above */ }; corral::Awaitable auto asyncRead(int fd, void* buf, size_t len) { return corral::makeAwaitable(fd, buf, len); } ``` -------------------------------- ### Introspection Tools API Documentation Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md API documentation for introspection tools, detailing how to collect async stack traces, dump the task tree, and annotate awaitables. ```APIDOC Introspection Tools: collectAsyncStackTrace(OutIt out) Produces an async stack trace by writing program addresses (uintptr_t) to `*out++`. The innermost `co_await` is first. Addresses can be converted to human-readable names using tools like `addr2line`. dumpTaskTree(OutIt out) Produces the entire task tree for the current async universe. Successive nodes are written as `TreeDumpElement`s to `*out++`. `TreeDumpElement::depth` indicates the relationship between nodes. Elements can be program addresses, textual names, or `std::type_info*` pointers. `ptr` holds the address of the coroutine frame or awaitable. annotate(std::string annotation, Awaitable&& awaitable) Annotates an awaitable with a custom string that appears in the async task tree. An artificial node with the annotation is created, with the wrapped awaitable as its only child. ``` -------------------------------- ### Run an Async Task with corral::run Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Shows how to execute an asynchronous task from the `main` function using `corral::run`. This function takes an event loop and an awaitable, running the loop until the task completes. ```cpp int main() { corral::run(io_service, wrap_hello()); } ``` -------------------------------- ### C++ Task Lifetime and Resource Management Example Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Demonstrates how C++ scopes and destructors work within corral tasks. It shows that a task's child operations are guaranteed to complete before the task exits, ensuring resources like file streams are properly managed and cleaned up, even during cancellation. ```cpp corral::Task foo() { std::ifstream ifs("/etc/passwd"); co_await publishOnFacebook(ifs); // publishOnFacebook() is guaranteed to be done with ifs, // so it can be safely closed here } ``` -------------------------------- ### Handling Cancellation and External State Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Illustrates how to manage task initialization and state when cancellation might occur. If a task is cancelled before signaling readiness, `TaskStarted` invocation is a no-op, and external variables captured by reference should be used for state management. ```cpp MyObject obj; CORRAL_WITH_NURSERY(n) { co_await n.start([&](corral::TaskStarted<> started) -> corral::Task<> { obj = makeObj(); started(); // ...work... }); // ...use obj... }; ``` -------------------------------- ### ThreadPool API Documentation Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md API details for the ThreadPool class, including constructor, run method, and CancelToken functionality. ```APIDOC ThreadPool: ThreadPool(EventLoopT& eventLoop, unsigned threadCount) Creates a thread pool with a fixed number of threads. Results are delivered to the thread running `eventLoop`. Requires a specialization of `ThreadNotification` for the event loop type. run(F&& function, Args&&... args) Submits a synchronous function to the thread pool for execution on a worker thread. Suspends the caller until the function completes and returns its result, re-raising any exceptions. Can only be called from the main thread. The function can optionally accept a `ThreadPool::CancelToken` to check for cancellation requests. CancelToken::operator bool() Returns true if cancellation has been requested for the coroutine suspended on `ThreadPool::run()`. It also confirms the cancellation request, discarding any return value or exception if true is returned. The coroutine remains suspended until the function returns. ``` -------------------------------- ### Awaiter State Machine States and Transitions Source: https://github.com/hudson-trading/corral/blob/master/doc/02_adapting.md Detailed description of each state in the awaiter state machine, including entry conditions, allowed operations, and resulting transitions. ```text * `Initial`: A new awaiter starts here. We don't know if it's ready yet, and need to check that before calling `await_suspend()`. We can also early-cancel before checking readiness. * Can call `await_early_cancel()`, transitioning to `Cancelled` if it returns true or `InitialCxlPend` otherwise. * Can call `await_ready()`, transitioning to `ReadyImmediately` if it returns true or `NotReady` otherwise. * `NotReady`: We know the awaiter is not ready, so we're allowed to suspend it now. We can also check the readiness redundantly, or early-cancel. * Can call `await_early_cancel()`, transitioning to `Cancelled` if it returns true or `CancelPending` otherwise. * Can call `await_ready()`, transitioning to `ReadyImmediately` if it returns true or staying in `NotReady` otherwise. * Otherwise, call `await_suspend()`. Transition to `Running` before making the call. If it returns true, treat that as equivalent to a resumption of the handle we passed to `await_suspend()`. * `ReadyImmediately`: The awaiter was ready without a suspension. Since it didn't suspend yet, we can still try an early cancel, or else we can proceed to consume the result. * Can call `await_ready()` redundantly but it is expected to continue returning true. * Can call `await_early_cancel()`, transitioning to `Cancelled` if it returns true or `ReadyAfterCancel` otherwise. * Otherwise, call `await_resume()` and transition to `Done`. * `InitialCxlPend`: We did an early-cancel and it did not succeed immediately. We still don't know if the awaiter is ready, so need to check that. * Call `await_ready()`, transitioning to `ReadyAfterCancel` if it returns true or `CancelPending` otherwise. * `CancelPending`: We did an early-cancel and it did not succeed immediately. We also know the awaiter is not ready, so we can suspend. And it's fine to check readiness redundantly as well. * Can call `await_ready()`, transitioning to `ReadyAfterCancel` if it returns true or staying in `CancelPending` otherwise. ``` -------------------------------- ### Establish TCP Connection with Fallback and Timeout Source: https://github.com/hudson-trading/corral/blob/master/README.md Establishes a TCP connection to one of two remote servers, whichever responds first, and returns the socket. It uses `corral::anyOf` to concurrently attempt connections and a timeout, propagating errors and returning the successful socket or throwing an exception if all attempts fail. ```C++ using tcp = boost::asio::tcp; boost::asio::io_service io_service; corral::Task myConnect(tcp::endpoint main, tcp::endpoint backup) { tcp::socket mainSock(io_service), backupSock(io_service); auto [mainErr, backupErr, timeout] = co_await corral::anyOf( // Main connection attempt mainSock.async_connect(main, corral::asio_nothrow_awaitable), // Backup connection, with staggered startup [&]() -> corral::Task { co_await corral::sleepFor(io_service, 100ms); co_return co_await backupSock.async_connect( backup, corral::asio_nothrow_awaitable); }, // Timeout on the whole thing corral::sleepFor(io_service, 3s)); if (mainErr && !*mainErr) { co_return mainSock; } else if (backupErr && !*backupErr) { co_return backupSock; } else { throw std::runtime_error("both connections failed"); } } ``` -------------------------------- ### Introspection Tools Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Tools for introspecting asynchronous execution, including collecting stack traces, dumping the task tree, and annotating awaitables. ```cpp template OutIt> OutIt collectAsyncStackTrace(OutIt out); struct TreeDumpElement { std::variant value; const void* ptr; int depth; }; template OutIt> Awaitable auto dumpTaskTree(OutIt out); template Awaitable auto annotate(std::string annotation, T&&); ``` -------------------------------- ### Nursery API Documentation Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Detailed API documentation for the Nursery class and related functions, explaining their usage, parameters, and behavior. ```APIDOC CORRAL_WITH_NURSERY(nursery) - Creates a nursery bound to the current scope within an async function. - The contents of the braces form the body of an async lambda that runs as the first task. - Use the `nursery` parameter (type `Nursery&`) to start other tasks. - The block must end with `co_return corral::join;` (wait for all tasks) or `co_return corral::cancel;` (cancel all tasks). Nursery::start(Callable callable, Args... args) /*1*/ - Starts `std::invoke(callable, args...)` in the nursery. Execution begins at the next `co_await` point. - The callable and arguments are moved into storage that persists until the task completes. - Use `std::ref()` or `std::cref()` for pass-by-reference, ensuring referent lifetimes. - Parameters: - callable: The function or callable object to execute. - args...: Arguments to pass to the callable. Nursery::start(Callable callable, Args... args) /*2*/ - Starts `std::invoke(callable, args..., TaskStarted{...})` in the nursery. - Suspends the caller until the task invokes its `TaskStarted` argument. - Until `TaskStarted` is invoked, the task is a child of the spawner; exceptions and cancellations are proxied. - If the task accepts `TaskStarted`, its invocation requires a value of type `T`, which becomes the result of `co_await start(...)`. - The result type `Ret` can be specified as a template argument if deduction fails. - Parameters: - callable: The function or callable object to execute. - args...: Arguments to pass to the callable, including a `TaskStarted` object. - Returns: An awaitable representing the started task. Nursery::cancel() - Requests cancellation of all tasks in the nursery, including those not yet started. - Tasks started after `cancel()` will still begin but are likely to be canceled upon their first `co_await`. Nursery::taskCount() const noexcept - Returns the number of tasks currently executing within the nursery. - Returns: The count of active tasks. Nursery::executor() const noexcept - Returns a pointer to the executor used by this nursery, typically inherited. - Returns `nullptr` after the nursery is closed. - Returns: A pointer to the `Executor`. openNursery(Nursery*& ptr) - Opens a new nursery and sets `ptr` to point to it. - The nursery remains open until the task is cancelled. - `ptr` is reset to `nullptr` after all nursery tasks complete or cancel and the nursery closes. - Parameters: - ptr: A reference to a `Nursery*` that will be set to the new nursery. - Returns: A `Task` representing the nursery opening operation. ``` -------------------------------- ### Corral Awaitable State Debugging Source: https://github.com/hudson-trading/corral/blob/master/doc/02_adapting.md Information on enabling runtime checks for the awaiter state machine using a preprocessor directive. ```text If you `#define CORRAL_AWAITABLE_STATE_DEBUG` before including any corral headers, the state machine will become explicit and runtime checks will be enabled that confirm the rules in this section are followed. (This is not recommended except for debugging purposes, as it is rather slow.) ``` -------------------------------- ### Asynchronous Cleanup with try/finally Source: https://github.com/hudson-trading/corral/blob/master/doc/01_getting_started.md Demonstrates how to perform asynchronous cleanup operations using Corral's try/finally construct, ensuring resources are released even if the task is cancelled or throws an exception. ```cpp struct AsyncFD { static corral::Awaitable auto open(std::string); corral::Awaitable auto close(); }; corral::Task workWithAsyncFD() { AsyncFD fd = co_await AsyncFD::open("..."); co_await try_([&]() -> Task { // do something with fd }).finally([&]() -> Task { co_await fd.close(); }); } ``` ```cpp corral::Task workWithAsyncFD() { AsyncFD fd = co_await AsyncFD::open("..."); CORRAL_TRY { // do something with fd } CORRAL_FINALLY { co_await fd.close(); }; // <- the trailing semicolon is required here } ``` -------------------------------- ### Corral CBPortal API Source: https://github.com/hudson-trading/corral/blob/master/doc/04_callback_bridging.md Documentation for corral::CBPortal and related functions like untilCBCalled and anyOf. Covers how to use these to manage callback-based asynchronous operations within C++ coroutines. ```APIDOC corral::CBPortal A mechanism to bridge callback-style asynchronous operations with awaitable tasks. Args: Template arguments specifying the types of arguments passed to the callback. corral::untilCBCalled(InitiatorFn initiator, CBPortal& portal) Waits for the first invocation of a callback provided by the initiator function. - initiator: A function that accepts a callback and arranges for it to be called. - portal: The CBPortal object to bridge with. - Returns: The arguments passed to the callback, wrapped in a tuple if multiple. corral::anyOf(CBPortal& p1, CBPortal& p2, ...) Waits for the first invocation across multiple CBPortals. - p1, p2, ...: CBPortal objects to monitor. - Returns: A tuple containing the result from the first portal to be invoked, with other results as std::nullopt. ``` -------------------------------- ### UnsafeNursery Constructor and Methods Source: https://github.com/hudson-trading/corral/blob/master/doc/05_reference.md Details the constructor and methods of UnsafeNursery. The constructor takes an event loop reference. `close()` cancels tasks, asserting on synchronous cancellation failures. `asyncClose()` cancels tasks and invokes a continuation upon completion. ```APIDOC UnsafeNursery(EventLoop auto& eventLoop) : Unlike a regular nursery, an `UnsafeNursery` can be defined as a member variable of a class. Like `corral::run()`, it needs a reference to the event loop that's being used to drive the tasks in the nursery. void close() : Cancels any tasks still running in the nursery. If any task cannot be synchronously cancelled, triggers an assert. After `close()` returns, submitting any further tasks to the nursery is disallowed (doing so would trigger an assert). ~UnsafeNursery() : Calls `close()` before destroying the nursery. void asyncClose(std::invocable<> auto continuation) : Cancels all tasks in the nursery. Once there are no tasks remaining, closes the nursery (so no new tasks can be started) and invokes the given continuation callback. The continuation callback may safely destroy the nursery. ```