mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-28 01:20:32 +00:00
Strengthen CoroTask_test assertions and timeout handling
- testExceptionPropagation now co_awaits an inner CoroTask<void> that throws and asserts the rethrown message, covering the CoroTask<void>::await_resume rethrow path; the old version could not distinguish a throw from a normal return. - testJobQueueAwaiter now actually awaits the JobQueueAwaiter struct (single use per coroutine, per the GCC-12 note) and asserts the full ordered step sequence instead of only the terminal value. - testValueException asserts the caught exception's message. - All waitFor() timeouts early-return on failure instead of falling through to join()/state reads (null-deref and TSAN-race hazards on timeout, plus the nSuspend_ assert in ~Env). - Remove dead shared_ptr locals in testCorrectOrder/testMultipleYields. - Add missing includes (<array>, <stdexcept>, <string>, <vector>, LocalValue.h, CoroTask.h).
This commit is contained in:
@@ -3,15 +3,21 @@
|
||||
|
||||
#include <xrpld/core/Config.h>
|
||||
|
||||
#include <xrpl/basics/LocalValue.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/core/CoroTask.h>
|
||||
#include <xrpl/core/Job.h>
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
#include <xrpl/core/JobQueueAwaiter.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::test {
|
||||
|
||||
@@ -42,9 +48,9 @@ namespace xrpl::test {
|
||||
* testVoidCompletion | CoroTask<void> basic lifecycle
|
||||
* testCorrectOrder | suspend() -> join() -> post() -> complete
|
||||
* testIncorrectOrder | post() before suspend() (race-safe path)
|
||||
* testJobQueueAwaiter | JobQueueAwaiter suspend + auto-repost
|
||||
* testJobQueueAwaiter | JobQueueAwaiter + yieldAndPost suspend/repost
|
||||
* testThreadSpecificStorage | LocalValue isolation across coroutines
|
||||
* testExceptionPropagation | unhandled_exception() in promise_type
|
||||
* testExceptionPropagation | CoroTask<void> exception via co_await
|
||||
* testMultipleYields | N sequential suspend/resume cycles
|
||||
* testValueReturn | CoroTask<T> co_return value
|
||||
* testValueException | CoroTask<T> exception via co_await
|
||||
@@ -124,7 +130,8 @@ public:
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
BEAST_EXPECT(!runner->runnable());
|
||||
}
|
||||
@@ -147,22 +154,20 @@ public:
|
||||
}));
|
||||
|
||||
Gate g1, g2;
|
||||
std::shared_ptr<JobQueue::CoroTaskRunner> r;
|
||||
auto runner = env.app().getJobQueue().postCoroTask(
|
||||
JtClient,
|
||||
"CoroTaskTest",
|
||||
[rp = &r, g1p = &g1, g2p = &g2](auto runner) -> CoroTask<void> {
|
||||
*rp = runner;
|
||||
JtClient, "CoroTaskTest", [g1p = &g1, g2p = &g2](auto runner) -> CoroTask<void> {
|
||||
g1p->signal();
|
||||
co_await runner->suspend();
|
||||
g2p->signal();
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
BEAST_EXPECT(g1.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g1.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
runner->post();
|
||||
BEAST_EXPECT(g2.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g2.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
}
|
||||
|
||||
@@ -195,7 +200,11 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* JobQueueAwaiter suspend + auto-repost across multiple yield points.
|
||||
* Suspend + auto-repost across multiple yield points, using the
|
||||
* external JobQueueAwaiter struct for the first suspension and the
|
||||
* inline yieldAndPost() awaiter for the second. JobQueueAwaiter is
|
||||
* used at only one co_await point per coroutine (see the GCC-12
|
||||
* multi-use warning in JobQueueAwaiter.h).
|
||||
*/
|
||||
void
|
||||
testJobQueueAwaiter()
|
||||
@@ -211,19 +220,22 @@ public:
|
||||
}));
|
||||
|
||||
Gate g;
|
||||
int step = 0;
|
||||
env.app().getJobQueue().postCoroTask(
|
||||
JtClient, "CoroTaskTest", [sp = &step, gp = &g](auto runner) -> CoroTask<void> {
|
||||
*sp = 1;
|
||||
std::vector<int> steps;
|
||||
auto runner = env.app().getJobQueue().postCoroTask(
|
||||
JtClient, "CoroTaskTest", [sp = &steps, gp = &g](auto runner) -> CoroTask<void> {
|
||||
sp->push_back(1);
|
||||
co_await JobQueueAwaiter{runner};
|
||||
sp->push_back(2);
|
||||
co_await runner->yieldAndPost();
|
||||
*sp = 2;
|
||||
co_await runner->yieldAndPost();
|
||||
*sp = 3;
|
||||
sp->push_back(3);
|
||||
gp->signal();
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
BEAST_EXPECT(step == 3);
|
||||
BEAST_EXPECT(runner);
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
BEAST_EXPECT(steps == std::vector<int>({1, 2, 3}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +266,8 @@ public:
|
||||
this->BEAST_EXPECT(*lv == -2);
|
||||
g.signal();
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
BEAST_EXPECT(*lv == -1);
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
@@ -276,13 +289,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& r : a)
|
||||
{
|
||||
r->post();
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
r->join();
|
||||
}
|
||||
for (auto const& r : a)
|
||||
@@ -295,13 +310,17 @@ public:
|
||||
this->BEAST_EXPECT(*lv == -2);
|
||||
g.signal();
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
BEAST_EXPECT(*lv == -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown in coroutine body is caught by
|
||||
* promise_type::unhandled_exception(). Coroutine completes.
|
||||
* An exception thrown in an awaited CoroTask<void> is rethrown into
|
||||
* the awaiting coroutine by await_resume(), with the original
|
||||
* message intact. (An exception escaping the top-level body has no
|
||||
* awaiter to rethrow it; it is captured by unhandled_exception()
|
||||
* and logged by CoroTaskRunner::resume().)
|
||||
*/
|
||||
void
|
||||
testExceptionPropagation()
|
||||
@@ -317,17 +336,29 @@ public:
|
||||
}));
|
||||
|
||||
Gate g;
|
||||
std::string what;
|
||||
auto runner = env.app().getJobQueue().postCoroTask(
|
||||
JtClient, "CoroTaskTest", [gp = &g](auto) -> CoroTask<void> {
|
||||
JtClient, "CoroTaskTest", [wp = &what, gp = &g](auto) -> CoroTask<void> {
|
||||
auto inner = []() -> CoroTask<void> {
|
||||
throw std::runtime_error("test exception");
|
||||
co_return;
|
||||
};
|
||||
try
|
||||
{
|
||||
co_await inner();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
*wp = e.what();
|
||||
}
|
||||
gp->signal();
|
||||
throw std::runtime_error("test exception");
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
// The exception is caught by promise_type::unhandled_exception()
|
||||
// and the coroutine is considered done
|
||||
BEAST_EXPECT(what == "test exception");
|
||||
BEAST_EXPECT(!runner->runnable());
|
||||
}
|
||||
|
||||
@@ -349,12 +380,8 @@ public:
|
||||
|
||||
Gate g;
|
||||
int counter = 0;
|
||||
std::shared_ptr<JobQueue::CoroTaskRunner> r;
|
||||
auto runner = env.app().getJobQueue().postCoroTask(
|
||||
JtClient,
|
||||
"CoroTaskTest",
|
||||
[rp = &r, cp = &counter, gp = &g](auto runner) -> CoroTask<void> {
|
||||
*rp = runner;
|
||||
JtClient, "CoroTaskTest", [cp = &counter, gp = &g](auto runner) -> CoroTask<void> {
|
||||
++(*cp);
|
||||
gp->signal();
|
||||
co_await runner->suspend();
|
||||
@@ -367,17 +394,20 @@ public:
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
BEAST_EXPECT(counter == 1);
|
||||
runner->join();
|
||||
|
||||
runner->post();
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
BEAST_EXPECT(counter == 2);
|
||||
runner->join();
|
||||
|
||||
runner->post();
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
BEAST_EXPECT(counter == 3);
|
||||
runner->join();
|
||||
BEAST_EXPECT(!runner->runnable());
|
||||
@@ -410,7 +440,8 @@ public:
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
BEAST_EXPECT(result == 42);
|
||||
BEAST_EXPECT(!runner->runnable());
|
||||
@@ -434,9 +465,9 @@ public:
|
||||
}));
|
||||
|
||||
Gate g;
|
||||
bool caught = false;
|
||||
std::string what;
|
||||
auto runner = env.app().getJobQueue().postCoroTask(
|
||||
JtClient, "CoroTaskTest", [cp = &caught, gp = &g](auto) -> CoroTask<void> {
|
||||
JtClient, "CoroTaskTest", [wp = &what, gp = &g](auto) -> CoroTask<void> {
|
||||
auto inner = []() -> CoroTask<int> {
|
||||
throw std::runtime_error("inner error");
|
||||
co_return 0;
|
||||
@@ -447,15 +478,16 @@ public:
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
*cp = true;
|
||||
*wp = e.what();
|
||||
}
|
||||
gp->signal();
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
BEAST_EXPECT(caught);
|
||||
BEAST_EXPECT(what == "inner error");
|
||||
BEAST_EXPECT(!runner->runnable());
|
||||
}
|
||||
|
||||
@@ -490,7 +522,8 @@ public:
|
||||
co_return;
|
||||
});
|
||||
BEAST_EXPECT(runner);
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
if (!BEAST_EXPECT(g.waitFor(5s)))
|
||||
return;
|
||||
runner->join();
|
||||
BEAST_EXPECT(result == 14); // (3 + 4) * 2
|
||||
BEAST_EXPECT(!runner->runnable());
|
||||
|
||||
Reference in New Issue
Block a user