async: context: allow many exceptions

Allow many spawned tasks to throw exceptions and queue them up,
re-throwing them one-by-one from `context::run` calls.  Similarly
enhance `context::scope` so that many `context::empty` completions
can complete with error.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I8744e0a3b8b0663ebd5ca26db0f5943688afe62a
diff --git a/test/async/context.cpp b/test/async/context.cpp
index b60cc38..afae280 100644
--- a/test/async/context.cpp
+++ b/test/async/context.cpp
@@ -13,10 +13,15 @@
         ctx.reset();
     }
 
-    void runToStop()
+    void spawnStop()
     {
         ctx->spawn(std::execution::just() |
                    std::execution::then([this]() { ctx->request_stop(); }));
+    }
+
+    void runToStop()
+    {
+        spawnStop();
         ctx->run();
     }
 
@@ -52,6 +57,24 @@
     ctx->run();
 }
 
+TEST_F(Context, SpawnManyThrowingTasks)
+{
+    static constexpr size_t count = 100;
+    for (size_t i = 0; i < count; ++i)
+    {
+        ctx->spawn(std::execution::just() | std::execution::then([]() {
+                       throw std::logic_error("Oops");
+                   }));
+    }
+    spawnStop();
+
+    for (size_t i = 0; i < count; ++i)
+    {
+        EXPECT_THROW(ctx->run(), std::logic_error);
+    }
+    ctx->run();
+}
+
 TEST_F(Context, SpawnDelayedTask)
 {
     using namespace std::literals;