13bool MemoryQueues::CreateMessageQueue(uint32& qid,
const char* name) {
16 uint32 newSize = DEFAULTQUEUESIZE;
18 char* data = MemoryManager::CreateAndLockNewSystemPage(newSize, pageID);
21 if (!MemoryMaps::CreateNewQueue(pageID, name, newQID)) {
22 MemoryManager::UnlockSystemBlock(pageID);
23 MemoryManager::DestroySystemPage(pageID);
28 header->
size = newSize;
30 header->pageID = pageID;
39 MemoryManager::UnlockSystemBlock(pageID);
44bool MemoryQueues::GetMessageQueueByName(uint32& qid,
const char* name) {
45 return MemoryMaps::GetQueueID(name, qid);
49bool MemoryQueues::AddMessageToQueue(uint32 qid,
DataMessage* msg) {
51 uint32 pageID = MemoryMaps::GetQueuePageID(qid);
53 char* data = MemoryManager::GetAndLockSystemBlock(pageID, dataSize);
57 uint32 msgSize = msg->getSize();
63 if (header->endPos >= header->startPos) {
64 spaceToEnd = qDataSize - header->endPos;
65 if (spaceToEnd >= msgSize) {
67 memcpy(qData+header->endPos, msg->data, msgSize);
68 header->endPos += msgSize;
72 else if (header->startPos >= msgSize) {
74 memset(qData+header->endPos, 0, spaceToEnd);
75 header->padding = spaceToEnd;
77 memcpy(qData, msg->data, msgSize);
78 header->endPos = msgSize;
83 MemoryManager::UnlockSystemBlock(pageID);
84 MemoryManager::ResizeSystemPage(pageID, dataSize*4);
85 data = MemoryManager::GetAndLockSystemBlock(pageID, dataSize);
90 if (header->startPos > 0) {
91 memcpy(qData, qData+header->startPos, header->endPos - header->startPos);
92 header->endPos = header->endPos - header->startPos;
95 MemoryManager::UnlockSystemBlock(pageID);
97 return AddMessageToQueue(qid, msg);
101 spaceToEnd = header->startPos - header->endPos;
102 if (spaceToEnd >= msgSize) {
104 memcpy(qData+header->endPos, msg->data, msgSize);
105 header->endPos += msgSize;
110 MemoryManager::UnlockSystemBlock(pageID);
111 MemoryManager::ResizeSystemPage(pageID, dataSize*4);
112 uint32 newDataSize = 0;
113 uint32 oldQDataSize = qDataSize;
114 data = MemoryManager::GetAndLockSystemBlock(pageID, newDataSize);
120 memcpy(qData+oldQDataSize-header->padding, qData, header->endPos);
121 header->endPos = oldQDataSize + header->endPos - header->padding;
131 MemoryManager::UnlockSystemBlock(pageID);
133 return AddMessageToQueue(qid, msg);
145 MemoryManager::UnlockSystemBlock(pageID);
150DataMessage* MemoryQueues::WaitForMessageQueue(uint32 qid, uint32 ms) {
151 uint32 pageID = MemoryMaps::GetQueuePageID(qid);
158 if (header->count == 0) {
161 MemoryManager::UnlockSystemBlock(pageID);
175 if (!(header = (
MessageQueueHeader*) MemoryManager::GetAndLockSystemBlock(pageID, dataSize)))
177 if (header->count > 0)
179 MemoryManager::UnlockSystemBlock(pageID);
189 char* msgData = qData + header->startPos;
193 header->startPos += msgDataSize;
202 header->startPos = msgDataSize;
207 MemoryManager::UnlockSystemBlock(pageID);
212 char* newData = (
char*)malloc(msgDataSize);
213 memcpy(newData, msgData, msgDataSize);
216 MemoryManager::UnlockSystemBlock(pageID);
221uint32 MemoryQueues::GetMessageQueueSize(uint32 qid) {
222 uint32 pageID = MemoryMaps::GetQueuePageID(qid);
224 char* data = MemoryManager::GetAndLockSystemBlock(pageID, dataSize);
229 uint32 qSize = header->
count;
231 MemoryManager::UnlockSystemBlock(pageID);
236bool MemoryQueues::DestroyMessageQueue(uint32 qid) {
237 uint32 pageID = MemoryMaps::GetQueuePageID(qid);
240 char* data = MemoryManager::GetAndLockSystemBlock(pageID, dataSize);
242 MemoryMaps::DeleteQueue(qid);
243 MemoryManager::UnlockSystemBlock(pageID);
245 MemoryManager::DestroySystemPage(pageID);
253bool MemoryQueues::AddRequest(
DataMessage* msg, uint32& reqID) {
255 RequestMapEntry* entry = MemoryMaps::CreateAndLockNewRequest(msg->getFrom(), msg->getTo(), reqID);
259 msg->setReference(reqID);
269 if (!MemoryMaps::GetProcessQueueID(procID,
REQQ_ID, qID)) {
270 MemoryMaps::UnlockRequestMap();
274 if (!AddMessageToQueue(qID, msg)) {
275 MemoryMaps::UnlockRequestMap();
279 MemoryMaps::UnlockRequestMap();
284bool MemoryQueues::AddReply(uint32 reqID,
bool success,
DataMessage* msg) {
291 uint64 msgEOL = msg->getEOL();
292 if (!MemoryManager::InsertMemoryBlock((
char*)msg->data, msg->getSize(), msgEOL, memID)) {
293 MemoryMaps::UnlockRequestMap();
296 entry->dataMessageID = memID;
297 entry->dataMessageEOL = msgEOL;
304 entry->dataMessageID = 0;
305 entry->dataMessageEOL = 0;
311 entry->status = status;
318 MemoryMaps::UnlockRequestMap();
323bool MemoryQueues::WaitForReply(uint32 reqID, uint32 ms, uint8& status,
DataMessage** outMsg) {
335 uint64 end = now + ms*1000;
336 MemoryMaps::UnlockRequestMap();
340 if (!sem->wait((uint32)((end-now)/1000))) {
345 entry = MemoryMaps::GetAndLockRequestEntry(reqID);
358 MemoryMaps::UnlockRequestMap();
363 status = entry->status;
367 if (entry->dataMessageEOL > now) {
370 char* data = MemoryManager::GetCopyMemoryBlock(entry->dataMessageID, size, eol);
374 if (*outMsg == NULL) {
380 MemoryMaps::UnlockRequestMap();
381 MemoryMaps::DeleteRequest(reqID);
386uint32 MemoryQueues::GetRequestCount() {
388 if (MemoryMaps::GetRequestCount(count))
408 sprintf(name,
"RequestSemaphore_%u", reqID);
418bool MemoryQueues::UnitTest() {
423 uint32 maxPageCount = 100;
424 if (!manager->create(0, maxPageCount)) {
431 if (!CreateMessageQueue(qid1)) {
435 uint32 count = 10000;
436 uint32 writeCount = 0;
437 uint32 readCount = 0;
445 for (n=0; n<count/4; n++) {
447 sprintf(str,
"Test%2d", writeCount);
448 msg->setString(
"TestEntry", str);
449 msg->setTime(
"TestTime", time);
450 msg->setString(
"TestEntry2", str);
451 msg->setTime(
"TestTime2", time);
452 if (!AddMessageToQueue(qid1, msg)) {
460 uint32 wroteP1 = count/4;
462 if ( (n = GetMessageQueueSize(qid1)) != count/4) {
463 unittest::fail(
"Queue contains %u messages instead of %u", n, count/4);
469 for (n=0; n<count/8; n++) {
470 if ( (msg = WaitForMessageQueue(qid1, 1000)) == NULL) {
474 sprintf(str,
"Test%2d", readCount);
475 if (strcmp(str, msg->getString(
"TestEntry")) != 0) {
476 unittest::fail(
"Message %u from Queue corrupted string '%s'", readCount, msg->getString(
"TestEntry"));
479 if (msg->getTime(
"TestTime") != time) {
480 unittest::fail(
"Message %u from Queue corrupted time '%llu'", readCount, msg->getTime(
"TestTime"));
483 if (strcmp(str, msg->getString(
"TestEntry2")) != 0) {
484 unittest::fail(
"Message %u from Queue corrupted string '%s'", readCount, msg->getString(
"TestEntry2"));
487 if (msg->getTime(
"TestTime2") != time) {
488 unittest::fail(
"Message %u from Queue corrupted time '%llu'", readCount, msg->getTime(
"TestTime2"));
495 uint32 readP1 = count/8;
497 if ( (n = GetMessageQueueSize(qid1)) != count/8) {
498 unittest::fail(
"Queue contains %u messages instead of %u", n, count/8);
504 for (n=0; n<3*count/4; n++) {
506 sprintf(str,
"Test%2d", writeCount);
507 msg->setString(
"TestEntry", str);
508 msg->setTime(
"TestTime", time);
509 msg->setString(
"TestEntry2", str);
510 msg->setTime(
"TestTime2", time);
511 if (!AddMessageToQueue(qid1, msg)) {
519 uint32 wroteTotal = wroteP1 + 3*count/4;
521 if ( (n = GetMessageQueueSize(qid1)) != 7*count/8) {
522 unittest::fail(
"Queue contains %u messages instead of %u", n, 7*count/8);
528 for (n=0; n<7*count/8; n++) {
529 if ( (msg = WaitForMessageQueue(qid1, 1000)) == NULL) {
533 sprintf(str,
"Test%2d", readCount);
534 if (strcmp(str, msg->getString(
"TestEntry")) != 0) {
535 unittest::fail(
"Message %u from Queue corrupted string '%s' (queue size %u)", readCount, msg->getString(
"TestEntry"), GetMessageQueueSize(qid1));
538 unittest::detail(
"Message %u from Queue '%s'", readCount, msg->getString(
"TestEntry"));
539 if (msg->getTime(
"TestTime") != time) {
540 unittest::fail(
"Message %u from Queue corrupted time '%llu'", readCount, msg->getTime(
"TestTime"));
543 if (strcmp(str, msg->getString(
"TestEntry2")) != 0) {
544 unittest::fail(
"Message %u from Queue corrupted string '%s'", readCount, msg->getString(
"TestEntry2"));
547 if (msg->getTime(
"TestTime2") != time) {
548 unittest::fail(
"Message %u from Queue corrupted time '%llu'", readCount, msg->getTime(
"TestTime2"));
555 uint32 readTotal = readP1 + 7*count/8;
557 if ( (n = GetMessageQueueSize(qid1)) != 0) {
562 if (!DestroyMessageQueue(qid1)) {
568 unittest::metric(
"queue_write_throughput", (
double)wroteTotal / wt_us * 1e6,
"msg/s",
true);
570 unittest::metric(
"queue_read_throughput", (
double)readTotal / rt_us * 1e6,
"msg/s",
true);
572 unittest::metric(
"queue_write_latency", wt_us / (
double)wroteTotal,
"us",
false);
574 unittest::metric(
"queue_read_latency", rt_us / (
double)readTotal,
"us",
false);
579 uint16 procID1, procID2;
580 uint32 compID1 = 1, compID2 = 2;
586 if (!MemoryMaps::CreateNewProcess(
"Proc1", procID1)) {
590 if (!MemoryMaps::CreateNewProcess(
"Proc2", procID2)) {
594 if (!MemoryQueues::CreateMessageQueue(qID)) {
598 if (!MemoryMaps::SetProcessQueueID(procID1,
MSGQ_ID, qID)) {
602 if (!MemoryQueues::CreateMessageQueue(qID)) {
606 if (!MemoryMaps::SetProcessQueueID(procID2,
REQQ_ID, qID)) {
612 if (compData1 == NULL) {
617 if (compData2 == NULL) {
625 if (!MemoryQueues::AddRequest(reqMsg, reqID)) {
631 if (!MemoryMaps::GetProcessQueueID(procID2,
REQQ_ID, qID)) {
635 reqMsg = MemoryQueues::WaitForMessageQueue(qID, 1000);
636 if (reqMsg == NULL) {
641 uint32 reqID2 = (uint32)reqMsg->getReference();
642 if (reqID != reqID2) {
648 if (!MemoryQueues::AddReply(reqID,
true, replyMsg)) {
657 if (!MemoryQueues::WaitForReply(reqID2, 1000, status, &replyMsg)) {
667 if (replyMsg == NULL) {
682bool MemoryQueues::UnitTestQueues() {
683 printf(
"Testing Multithreaded Memory Queues...\n\n");
687 if (!manager->create(0, 30)) {
688 fprintf(stderr,
"MemoryManager init() failed...\n");
698 if (!MemoryQueues::CreateMessageQueue(q1,
"Q1")) {
699 LogPrint(0, 0, 0,
"[1] Could not create Test Queue 1...");
703 if (!MemoryQueues::CreateMessageQueue(q2,
"Q2")) {
704 LogPrint(0, 0, 0,
"[1] Could not create Test Queue 2...");
709 uint32 queueTestThreadID;
711 LogPrint(0, 0, 0,
"[1] Could not create Queue Test thread...");
717 msg->setInt(
"Counter", 0);
719 if (!MemoryQueues::AddMessageToQueue(q1, msg)) {
720 LogPrint(0, 0, 0,
"[1] Could not add initial message to Q1...");
729 if (msg = MemoryQueues::WaitForMessageQueue(q2, 50)) {
731 msg->getInt(
"Counter", c);
732 if (c && (c % 99999 == 0)) {
737 msg->setInt(
"Counter", c+1);
739 if (!MemoryQueues::AddMessageToQueue(q1, msg)) {
740 LogPrint(0, 0, 0,
"[1] Could not add message %lld to Q1...", c+1);
746 LogPrint(0,0,0,
"[1 - %lld] Wait: %lld Add: %lld Total: %lld Msg: %lld\n", c+1, t2-t1, t3-t2, t3-t1, t3-msg->getSendTime());
751 LogPrint(0,0,0,
"[1 - %lld] Timeout: %lld\n", c+1, t3-t1);
767 if (!MemoryQueues::GetMessageQueueByName(q1,
"Q1")) {
768 LogPrint(0, 0, 0,
"[2] Could not Get Test Queue 1...");
772 if (!MemoryQueues::GetMessageQueueByName(q2,
"Q2")) {
773 LogPrint(0, 0, 0,
"[2] Could not Get Test Queue 2...");
779 if (msg = MemoryQueues::WaitForMessageQueue(q1, 50)) {
781 msg->getInt(
"Counter", c);
782 msg->setInt(
"Counter", c+1);
784 if (!MemoryQueues::AddMessageToQueue(q2, msg)) {
785 LogPrint(0, 0, 0,
"[2] Could not add message %lld to Q2...", c+1);
791 LogPrint(0,0,0,
"[2 - %lld] Wait: %lld Add: %lld Total: %lld Msg: %lld\n", c+1, t2-t1, t3-t2, t3-t1, t3-msg->getSendTime());
796 LogPrint(0,0,0,
"[2 - %lld] Timeout: %lld\n", c+1, t3-t1);
803void Register_MemoryQueues_Tests() {
805 "Message queue add/wait/size, request/reply roundtrip",
"memory");
#define REQ_FAILED_NODATA
#define REQ_SUCCESS_NODATA
#define NODE_PROCESS_ID
Reserved process id of the node process itself.
#define MSGQ_ID
Data-message queue slot.
#define REQQ_ID
Request queue slot.
Ring-buffer message queues stored in shared memory, plus a static request/reply facility.
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Small, dependency-free unit test harness used by all CMSDK object tests.
#define thread_ret_val(ret)
#define THREAD_FUNCTION_CALL
Legacy accessor wrapping one component record inside a shared page.
static ComponentData * CreateComponent(uint32 id, const char *name, uint32 size, uint16 nodeID, uint16 procID)
static uint16 GetComponentProcessID(uint32 cid)
The central Psyclone data container: a self-contained binary message with typed, named user entries.
Top-level facade of the shared-memory subsystem for one process.
static MemoryManager * Singleton
Per-process singleton instance, set by the constructor.
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
Counting semaphore, optionally named for cross-process use.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
bool SignalSemaphore(const char *name)
Signal a named global semaphore.
bool WaitForSemaphore(const char *name, uint32 ms, bool autocreate=true)
Wait on a named global semaphore.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
static struct PsyType CTRL_TEST
static THREAD_RET THREAD_FUNCTION_CALL QueueTest(THREAD_ARG arg)
Request-map entry tracking one cross-component request/reply transaction.