diff --git a/Tests/UnitTests/Tests/AsyncBlockTests.cpp b/Tests/UnitTests/Tests/AsyncBlockTests.cpp index 7a77556d..06fd4ae7 100644 --- a/Tests/UnitTests/Tests/AsyncBlockTests.cpp +++ b/Tests/UnitTests/Tests/AsyncBlockTests.cpp @@ -7,6 +7,7 @@ #include "XAsyncProviderPriv.h" #include "XTaskQueue.h" #include "XTaskQueuePriv.h" +#include #define TEST_CLASS_OWNER L"brianpe" @@ -79,13 +80,43 @@ DEFINE_TEST_CLASS(AsyncBlockTests) DWORD result = 0; DWORD iterationWait = 0; DWORD workThread = 0; - std::vector opCodes; + + // Fixed-capacity lock-free opcode log for concurrent append + static constexpr size_t MAX_OPCODES = 16; + std::array, MAX_OPCODES> opCodesArray{}; + std::atomic opCodesCount{ 0 }; + std::atomic inWork = 0; std::atomic refs = 0; std::atomic canceled = false; void AddRef() { refs++; } void Release() { if (--refs == 0) delete this; } + + // Thread-safe append operation + void RecordOp(XAsyncOp op) + { + size_t idx = opCodesCount.fetch_add(1, std::memory_order_relaxed); + if (idx < MAX_OPCODES) + { + opCodesArray[idx].store(op, std::memory_order_release); + } + // Silently drop if overflow (test will fail on verification anyway) + } + + // Snapshot current opcodes into a vector for verification + std::vector GetOpCodes() const + { + size_t count = opCodesCount.load(std::memory_order_acquire); + count = (count < MAX_OPCODES) ? count : MAX_OPCODES; + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + result.push_back(opCodesArray[i].load(std::memory_order_acquire)); + } + return result; + } }; static PCWSTR OpName(XAsyncOp op) @@ -117,7 +148,7 @@ DEFINE_TEST_CLASS(AsyncBlockTests) { FactorialCallData* d = (FactorialCallData*)data->context; - d->opCodes.push_back(opCode); + d->RecordOp(opCode); switch (opCode) { @@ -166,7 +197,7 @@ DEFINE_TEST_CLASS(AsyncBlockTests) { FactorialCallData* d = (FactorialCallData*)data->context; - d->opCodes.push_back(opCode); + d->RecordOp(opCode); switch (opCode) { @@ -391,7 +422,7 @@ DEFINE_TEST_CLASS(AsyncBlockTests) ops.push_back(XAsyncOp::GetResult); ops.push_back(XAsyncOp::Cleanup); - VerifyOps(data.Ref->opCodes, ops); + VerifyOps(data.Ref->GetOpCodes(), ops); VERIFY_QUEUE_EMPTY(queue); } @@ -480,7 +511,7 @@ DEFINE_TEST_CLASS(AsyncBlockTests) ops.push_back(XAsyncOp::GetResult); ops.push_back(XAsyncOp::Cleanup); - VerifyOps(data.Ref->opCodes, ops); + VerifyOps(data.Ref->GetOpCodes(), ops); VERIFY_QUEUE_EMPTY(queue); } @@ -554,8 +585,9 @@ DEFINE_TEST_CLASS(AsyncBlockTests) Sleep(500); VERIFY_ARE_EQUAL(E_ABORT, hrCallback); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::Cancel); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::Cleanup); + auto opCodes = data.Ref->GetOpCodes(); + VerifyHasOp(opCodes, XAsyncOp::Cancel); + VerifyHasOp(opCodes, XAsyncOp::Cleanup); VERIFY_QUEUE_EMPTY(queue); } @@ -587,9 +619,10 @@ DEFINE_TEST_CLASS(AsyncBlockTests) VERIFY_ARE_EQUAL(XAsyncGetStatus(&async, true), E_ABORT); XTaskQueueDispatch(queue, XTaskQueuePort::Completion, 700); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::Cancel); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::Cleanup); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::DoWork); + auto opCodes = data.Ref->GetOpCodes(); + VerifyHasOp(opCodes, XAsyncOp::Cancel); + VerifyHasOp(opCodes, XAsyncOp::Cleanup); + VerifyHasOp(opCodes, XAsyncOp::DoWork); VERIFY_QUEUE_EMPTY(queue); } @@ -620,9 +653,10 @@ DEFINE_TEST_CLASS(AsyncBlockTests) VERIFY_ARE_EQUAL(XAsyncGetStatus(&async, true), E_ABORT); Sleep(500); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::Cancel); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::Cleanup); - VerifyHasOp(data.Ref->opCodes, XAsyncOp::DoWork); + auto opCodes = data.Ref->GetOpCodes(); + VerifyHasOp(opCodes, XAsyncOp::Cancel); + VerifyHasOp(opCodes, XAsyncOp::Cleanup); + VerifyHasOp(opCodes, XAsyncOp::DoWork); VERIFY_QUEUE_EMPTY(queue); }