Fix JobQueue_test resume() contract violation and Coroutine_test timeout hazards

- PostCoroTest2 called runner->resume() directly from the test thread,
  violating the documented precondition on CoroTaskRunner::resume() (a
  post() must precede every resume) and driving runCount_ negative.
  Rewrite the loop as post()+join(); the non-atomic yieldCount now also
  verifies the happens-before edge join() provides via mutexRun_.
- PostCoroTest3 wrote false into an already-false flag, so it could
  not detect the coroutine running after stop(). Write true and assert
  the flag stays false.
- Coroutine_test: early-return when a Gate waitFor() times out instead
  of falling through to c->join()/a[i]->join(), which would deref a
  null shared_ptr (correctOrder, threadSpecificStorage first loop) or
  block indefinitely (second loop).
This commit is contained in:
Pratik Mankawde
2026-07-27 10:29:57 +01:00
parent 22ed85c3ad
commit 25b9b4d27d
2 changed files with 18 additions and 7 deletions

View File

@@ -78,7 +78,8 @@ public:
g2p->signal();
co_return;
});
BEAST_EXPECT(g1.waitFor(5s));
if (!BEAST_EXPECT(g1.waitFor(5s)))
return;
c->join();
c->post();
BEAST_EXPECT(g2.waitFor(5s));
@@ -158,13 +159,15 @@ public:
this->BEAST_EXPECT(**lvp == id);
co_return;
});
BEAST_EXPECT(g.waitFor(5s));
if (!BEAST_EXPECT(g.waitFor(5s)))
return;
a[i]->join();
}
for (auto const& c : a)
{
c->post();
BEAST_EXPECT(g.waitFor(5s));
if (!BEAST_EXPECT(g.waitFor(5s)))
return;
c->join();
}
for (auto const& c : a)

View File

@@ -87,7 +87,7 @@ class JobQueue_test : public beast::unit_test::Suite
BEAST_EXPECT(yieldCount == 4);
}
{
// Test repeated resume()s until the coroutine completes.
// Test repeated post()+join()s until the coroutine completes.
int yieldCount{0};
auto const runner = jQueue.postCoroTask(
JtClient, "PostCoroTest2", [ycp = &yieldCount](auto runner) -> CoroTask<void> {
@@ -106,11 +106,18 @@ class JobQueue_test : public beast::unit_test::Suite
// Wait for the Job to run and yield.
runner->join();
// Now resume until the CoroTaskRunner says it is done.
// Now post()+join() until the CoroTaskRunner says it is done.
// resume() requires a prior post() (see the precondition on
// CoroTaskRunner::resume()), so the posted job performs the
// resume and join() blocks until it completes. yieldCount is
// deliberately not atomic: the mutexRun_ handoff inside join()
// must provide the happens-before edge that makes the
// increment visible to this thread.
int old = yieldCount;
while (runner->runnable())
{
runner->resume(); // Resume runs synchronously on this thread.
BEAST_EXPECT(runner->post());
runner->join();
BEAST_EXPECT(++old == yieldCount);
}
BEAST_EXPECT(yieldCount == 4);
@@ -128,10 +135,11 @@ class JobQueue_test : public beast::unit_test::Suite
bool unprotected = false;
auto const runner = jQueue.postCoroTask(
JtClient, "PostCoroTest3", [up = &unprotected](auto) -> CoroTask<void> {
*up = false;
*up = true;
co_return;
});
BEAST_EXPECT(runner == nullptr);
BEAST_EXPECT(unprotected == false);
}
}