CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
MemoryRequestServer.cpp
Go to the documentation of this file.
1
4
6#include "PsyTime.h"
7#include "UnitTestFramework.h"
8
9namespace cmlabs {
10
12 serverHeader = NULL;
13 mutex = NULL;
14 requestCon = NULL;
15}
16
20
21
22bool MemoryRequestServer::init(const char* memoryName, uint32 maxConnectionCount, uint32 maxQueueSize, uint32 maxRequestCount) {
23 uint32 masterSize = sizeof(RequestServerHeader) +
24 (maxConnectionCount * (sizeof(RequestConnectionHeader) + sizeof(RequestQueueHeader) + maxQueueSize)) +
25 sizeof(RequestStatusHeader) + (maxRequestCount * sizeof(RequestStatusEntry));
26
27 mutex = new utils::Mutex(utils::StringFormat("%s_Mutex", memoryName).c_str());
28 if (!mutex || !mutex->enter(1000))
29 return false;
30
31 // Allocate main shared memory segment
32 serverHeader = (RequestServerHeader*) utils::CreateSharedMemorySegment(memoryName, masterSize);
33 if (!serverHeader) {
34 mutex->leave();
35 return false;
36 }
37
38 // Init memory
39 memset(serverHeader, 0, masterSize);
40 serverHeader->cid = MEMORYREQUESTSERVERID;
41 serverHeader->createdTime = GetTimeNow();
42 serverHeader->lastUpdateTime = serverHeader->createdTime;
43 serverHeader->maxConnectionCount = maxConnectionCount;
44 serverHeader->eachConnectionSize = sizeof(RequestConnectionHeader) + sizeof(RequestQueueHeader) + maxQueueSize;
45 serverHeader->maxQueueSize = maxQueueSize;
46 serverHeader->maxRequestCount = maxRequestCount;
47 serverHeader->size = masterSize;
48 serverHeader->status = MEMORYREQUESTSERVER_INIT;
49 utils::strcpyavail(serverHeader->name, memoryName, 255, true);
50
51 char* requestHeader = ((char*)serverHeader)+sizeof(RequestServerHeader)
52 + (serverHeader->maxConnectionCount * serverHeader->eachConnectionSize);
53 if (!MemoryRequestQueues::InitRequestMap(requestHeader, serverHeader->maxRequestCount)) {
54 shutdown();
55 return false;
56 }
57
58 // All queues, requests and connections are zero
59 mutex->leave();
60
61 requestCon = new MemoryRequestConnection();
62 if (!requestCon->connect(memoryName)) {
63 shutdown();
64 return false;
65 }
66
67 return true;
68}
69
71 if (requestCon) {
72 MemoryRequestConnection* temp = requestCon;
73 requestCon = NULL;
74 delete(temp);
75 }
76 if (!mutex)
77 return true;
78 if (!mutex->enter(1000))
79 return false;
80 if (serverHeader) {
81 utils::CloseSharedMemorySegment((char*) serverHeader, serverHeader->size);
82 serverHeader = NULL;
83 }
84 mutex->leave();
85 return true;
86}
87
88
90 if (!requestCon) return NULL;
91 return requestCon->waitForRequest(ms);
92}
93
94bool MemoryRequestServer::setRequestStatus(uint64 id, uint16 status) {
95 if (!requestCon) return false;
96 return requestCon->setRequestStatus(id, status);
97}
98
100 if (!requestCon) return false;
101 return requestCon->replyToRequest(id, msg);
102}
103
104
105
106bool MemoryRequestServer::maintenance() {
107 return true;
108}
109
111
112 // Self-contained: server + two client connections share an in-process named
113 // shared-memory segment ("TestServer"). All waits are bounded (<=500ms), so
114 // the test always terminates. Loop counts kept modest for a 1-2s runtime
115 // while still producing a stable throughput metric.
116 uint32 count = 10, queueCount = 10, n, m;
117
118 unittest::progress(5, "init server");
120 // Modest sizing: a few connections with a small per-connection queue is
121 // plenty for this test and keeps the shared segment small.
122 if (!server->init("TestServer", 4, 256*1024, 256)) {
123 unittest::fail("Couldn't initialise MemoryRequestServer");
124 delete(server);
125 return false;
126 }
127
128 uint64 createdTime, lastUpdateTime;
129 DataMessage* reqMsg;
130 uint64* reqIDs1 = new uint64[queueCount];
131 uint64* reqIDs2 = new uint64[queueCount];
134
135 uint64 startTime = 0;
136 uint32 totalRequests = 0;
137
138 unittest::progress(15, "connect clients");
139 if (!con1->connect("TestServer")) {
140 unittest::fail("Couldn't initialise MemoryRequestConnection 1");
141 goto testfail;
142 }
143
144 if (!con2->connect("TestServer")) {
145 unittest::fail("Couldn't initialise MemoryRequestConnection 2");
146 goto testfail;
147 }
148
149 if (reqMsg = server->waitForRequest(100)) {
150 unittest::fail("Server got request when no request expected");
151 delete(reqMsg);
152 goto testfail;
153 }
154
155 unittest::progress(25, "request/reply exchange");
156 startTime = GetTimeNow();
157 for (n=0; n<count; n++) {
158 reqMsg = new DataMessage();
159 reqMsg->setTo(1);
160
161 for (m=0; m<queueCount; m++) {
162 if (!(reqIDs1[m] = con1->makeRequest(reqMsg))) {
163 unittest::fail("Con1 couldn't make request [run %u of %u, entry %u of %u]", n, count, m, queueCount);
164 delete(reqMsg);
165 goto testfail;
166 }
167 if (!(reqIDs2[m] = con2->makeRequest(reqMsg))) {
168 unittest::fail("Con2 couldn't make request [run %u of %u, entry %u of %u]", n, count, m, queueCount);
169 delete(reqMsg);
170 goto testfail;
171 }
172 }
173 delete(reqMsg);
174
175 for (m=0; m<queueCount; m++) {
176 if (con1->getRequestStatus(reqIDs1[m], createdTime, lastUpdateTime) < MEMORYREQUEST_SUBMITTED) {
177 unittest::fail("Con1 request not submitted [run %u of %u, entry %u of %u]", n, count, m, queueCount);
178 goto testfail;
179 }
180 if (con2->getRequestStatus(reqIDs2[m], createdTime, lastUpdateTime) < MEMORYREQUEST_SUBMITTED) {
181 unittest::fail("Con2 request not submitted [run %u of %u, entry %u of %u]", n, count, m, queueCount);
182 goto testfail;
183 }
184 }
185
186 for (m=0; m<queueCount; m++) {
187 if (!(reqMsg = server->waitForRequest(100))) {
188 unittest::fail("Server didn't receive request from con1 [run %u of %u, entry %u of %u]", n, count, m, queueCount);
189 goto testfail;
190 }
191 reqMsg->setTo(reqMsg->getFrom());
192 if (!server->replyToRequest(reqMsg->getReference(), reqMsg)) {
193 unittest::fail("Server couldn't reply to request from con1 [run %u of %u, entry %u of %u]", n, count, m, queueCount);
194 delete(reqMsg);
195 goto testfail;
196 }
197 delete(reqMsg);
198
199 if (!(reqMsg = server->waitForRequest(100))) {
200 unittest::fail("Server didn't receive request from con2 [run %u of %u, entry %u of %u]", n, count, m, queueCount);
201 goto testfail;
202 }
203 reqMsg->setTo(reqMsg->getFrom());
204 if (!server->replyToRequest(reqMsg->getReference(), reqMsg)) {
205 unittest::fail("Server couldn't reply to request from con2 [run %u of %u, entry %u of %u]", n, count, m, queueCount);
206 delete(reqMsg);
207 goto testfail;
208 }
209 delete(reqMsg);
210 }
211
212 for (m=0; m<queueCount; m++) {
213 if (con1->getRequestStatus(reqIDs1[m], createdTime, lastUpdateTime) < MEMORYREQUEST_REPLIED) {
214 unittest::fail("Con1 request not replied [run %u of %u, entry %u of %u]", n, count, m, queueCount);
215 goto testfail;
216 }
217 if (con2->getRequestStatus(reqIDs2[m], createdTime, lastUpdateTime) < MEMORYREQUEST_REPLIED) {
218 unittest::fail("Con2 request not replied [run %u of %u, entry %u of %u]", n, count, m, queueCount);
219 goto testfail;
220 }
221 }
222
223 for (m=0; m<queueCount; m++) {
224 if (!(reqMsg = con1->waitForRequestReply(reqIDs1[m], 500))) {
225 unittest::fail("Con1 didn't get reply [run %u of %u, entry %u of %u]", n, count, m, queueCount);
226 goto testfail;
227 }
228 delete(reqMsg);
229 if (!(reqMsg = con2->waitForRequestReply(reqIDs2[m], 500))) {
230 unittest::fail("Con2 didn't get reply [run %u of %u, entry %u of %u]", n, count, m, queueCount);
231 goto testfail;
232 }
233 delete(reqMsg);
234 }
235
236 totalRequests += 2 * queueCount;
237 unittest::detail("run %u of %u complete (%u requests so far)", n + 1, count, totalRequests);
238 unittest::progress(25 + (uint32)((uint64)(n + 1) * 65 / count), "request/reply exchange");
239 }
240
241 {
242 double us = (double)(GetTimeNow() - startTime);
243 if (us > 0)
244 unittest::metric("request_reply_throughput", (double)totalRequests / us * 1e6, "req/s", true);
245 }
246
247 unittest::progress(95, "shutdown");
248 delete [] reqIDs1;
249 delete [] reqIDs2;
250 delete(con2);
251 delete(con1);
252 delete(server);
253 unittest::progress(100, "done");
254 return true;
255
256testfail:
257 delete [] reqIDs1;
258 delete [] reqIDs2;
259 delete(con2);
260 delete(con1);
261 delete(server);
262 return false;
263}
264
267 "Shared-memory request server/connection request-reply round-trip", "memory");
268}
269
270} // namespace cmlabs
#define MEMORYREQUESTSERVER_INIT
Server status value: segment initialised and accepting connections.
#define MEMORYREQUEST_SUBMITTED
Placed on the server's queue.
#define MEMORYREQUEST_REPLIED
Reply enqueued to the requester.
Server side of the named shared-memory request/reply service.
#define MEMORYREQUESTSERVERID
Definition ObjectIDs.h:72
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.
The central Psyclone data container: a self-contained binary message with typed, named user entries.
uint32 getFrom()
getFrom() Get the sender id
uint64 getReference()
getReference() Get and return message reference id
bool setTo(uint32 to)
setTo(uint32 to)
Client endpoint connected to a named shared-memory request server.
uint16 getRequestStatus(uint64 id, uint64 &createdTime, uint64 &lastUpdateTime)
Query a request's status.
DataMessage * waitForRequestReply(uint32 ms)
Wait for the reply to this connection's most recent request.
bool connect(const char *memoryName)
Attach to the named server segment and claim a connection slot.
uint64 makeRequest(DataMessage *msg)
Submit a request to the server.
static bool InitRequestMap(char *data, uint32 count)
Initialise a status map for count requests.
static bool UnitTest()
Self-test of the request/reply round trip.
bool replyToRequest(uint64 id, DataMessage *msg)
Send the reply for request id (copied; caller keeps ownership).
bool shutdown()
Detach and mark the server stopped.
DataMessage * waitForRequest(uint32 ms)
Block up to ms milliseconds for the next client request.
bool init(const char *memoryName, uint32 maxConnectionCount, uint32 maxQueueSize, uint32 maxRequestCount)
Create the named server segment.
bool setRequestStatus(uint64 id, uint16 status)
Advance request id's MEMORYREQUEST_* status.
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.
Recursive mutual-exclusion lock, optionally named for cross-process use.
Definition Utils.h:463
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
char * CreateSharedMemorySegment(const char *name, uint64 size, bool force=false)
Create a named shared memory segment and map it into this process.
Definition Utils.cpp:2156
bool CloseSharedMemorySegment(char *data, uint64 size)
Unmap a segment previously created/opened here.
Definition Utils.cpp:2374
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:6626
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:6056
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.
void Register_MemoryRequestServer_Tests()
Header of one client connection area within a request-server segment.
Header of one circular request queue; followed by serialized DataMessages between startPos and endPos...
Header of a request-server segment: identity, limits and layout parameters.
One request transaction's status record.
Header of the request-status table; followed by a bitfield and countTotal RequestStatusEntry records.