async: context: allow run re-entrance

Switch the context call so that it can be re-entrant.  A single call
to `run` will either:

    1. Process until the context is successfully stopped.
    2. An exception is raised from a spawned task.

If an exception is raised, it could be caught outside the run call
and then run called again.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Icd05df421a1f0592bd2390fbe8e21a16252eafed
diff --git a/test/async/context.cpp b/test/async/context.cpp
index 34e5bb0..b60cc38 100644
--- a/test/async/context.cpp
+++ b/test/async/context.cpp
@@ -34,6 +34,24 @@
     runToStop();
 }
 
+TEST_F(Context, ReentrantRun)
+{
+    runToStop();
+    for (int i = 0; i < 100; ++i)
+    {
+        ctx->run();
+    }
+}
+
+TEST_F(Context, SpawnThrowingTask)
+{
+    ctx->spawn(std::execution::just() |
+               std::execution::then([]() { throw std::logic_error("Oops"); }));
+
+    EXPECT_THROW(runToStop(), std::logic_error);
+    ctx->run();
+}
+
 TEST_F(Context, SpawnDelayedTask)
 {
     using namespace std::literals;
@@ -99,6 +117,7 @@
                std::execution::then([&m](...) { m.reset(); }));
 
     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
+    EXPECT_NO_THROW(ctx->run());
     EXPECT_FALSE(ran);
 }
 
@@ -127,5 +146,6 @@
                std::execution::then([&]() { m.reset(); }));
 
     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
+    EXPECT_NO_THROW(ctx->run());
     EXPECT_FALSE(ran);
 }