CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
PsySpace.cpp
Go to the documentation of this file.
1
8#include "PsySpace.h"
9
10namespace cmlabs {
11
12
14 this->interval = interval;
15 this->start = start;
16 this->end = end;
17}
18
21
22
24 timer = new utils::Timer();
25}
26
28 mutex.enter();
29 std::map<uint32, TimeQueueSchedule*>::iterator it, itEnd;
30 for (it = schedules.begin(), itEnd = schedules.end(); it!=itEnd; it++)
31 delete(it->second);
32 delete(timer);
33 mutex.leave();
34}
35
37 mutex.enter();
38 if (schedules[schedule->id]) {
39 mutex.leave();
40 return false;
41 }
42 schedules[schedule->id] = schedule;
43 mutex.leave();
44 return timer->addTimer(schedule->id, schedule->interval, schedule->start, schedule->end);
45}
46
48 mutex.enter();
49 TimeQueueSchedule* schedule = schedules[id];
50 if (!schedule) {
51 mutex.leave();
52 return true;
53 }
54 timer->removeTimer(id);
55 schedules.erase(id);
56 delete(schedule);
57 mutex.leave();
58 return true;
59}
60
62
63 uint32 id;
64 uint64 time;
65 if (!timer->waitForTimer(ms, id, time))
66 return NULL;
67
68 mutex.enter();
69 TimeQueueSchedule* schedule = schedules[id];
70 if (!schedule) {
71 mutex.leave();
72 return NULL;
73 }
74
75 DataMessage* msg = new DataMessage(schedule->msgType, 0);
76 if (schedule->msgTag)
77 msg->setTag(schedule->msgTag);
78 mutex.leave();
79 return msg;
80}
81
82
83
84
85
86
87
88PsySpace::PsySpace(const char* name, bool isAdHoc, uint16 procID, bool isLocal) {
89 isMaster = true;
90 manager = NULL;
91 masterCreatedTime = 0;
92 timeQ = new TimeQueue();
93 this->isAdHoc = isAdHoc;
94 if (name)
95 this->name = utils::TextTrimQuotes(name);
96 else
97 this->name = utils::StringFormat("Unnamed_Space_%u", (uint32)utils::RandomValue(MAXVALUINT32));
98 this->isLocal = isLocal;
99 this->procID = procID;
100 finishedShuttingDown = false;
101
102 memset(waitCounters, 0, 3*sizeof(uint32));
103 memset(procCounters, 0, 3*sizeof(uint32));
104
105 threadPoolWaitLowerThreshold = 5;
106 threadPoolWaitUpperThreshold = 20;
107 threadPoolWaitIncrement = 5;
108 lastThreadPoolCheck = GetTimeNow();
109 threadPoolCheckInterval = 5000;
110
111 internalCranks["Simple"] = Internal_Simple;
112 internalCranks["Ping"] = Internal_Ping;
113 internalCranks["Pong"] = Internal_Pong;
114 internalCranks["Shutdown"] = Internal_Shutdown;
115 internalCranks["Print"] = Internal_Print;
116 internalCranks["Time"] = Internal_Time;
117 internalCranks["SignalPing"] = Internal_SignalPing;
118 internalCranks["SignalPong"] = Internal_SignalPong;
119 internalCranks["RetrieveTest"] = Internal_RetrieveTest;
120 internalCranks["MessageScript"] = Internal_MessageScript;
121 internalCranks["QueryTest"] = Internal_QueryTest;
122 internalCranks["StatsLog"] = Internal_StatsLog;
123 internalCranks["BitmapPoster"] = Internal_BitmapPoster;
124 internalCranks["MessageToggler"] = Internal_MessageToggler;
125 internalCranks["MessageTypeConverter"] = Internal_MessageTypeConverter;
126}
127
129 reset();
130 if (timeQ) delete(timeQ);
131 timeQ = NULL;
132}
133
134bool PsySpace::connect(uint16 systemID, bool isMaster, const char* cmdline) {
135
136 // LogPrint(0, LOG_SPACE, 1, "PsySpace '%s' starting up, connecting to local Node %u...", name.c_str(), systemID);
137
138 this->isMaster = isMaster;
139
140 // Connect to MemoryManager
141 if (manager) delete(manager);
142 manager = new MemoryManager();
143 if (!manager->connect(systemID, isMaster)) {
144 // LogPrint(0, LOG_SPACE, 0, "PsySpace couldn't connect to local Node %u...", systemID);
145 delete(manager);
146 manager = NULL;
147 return false;
148 }
149 finishedShuttingDown = false;
150
151 uint16 checkID;
152 if (!isAdHoc) {
153 if (!manager->processMemory->getProcessID(name.c_str(), checkID)) {
154 LogPrint(0, LOG_SPACE, 0, "Could not find process '%s'...", name.c_str());
155 return false;
156 }
157 if (!procID)
158 procID = checkID;
159 else if (procID != checkID) {
160 LogPrint(0, LOG_SPACE, 0, "Process ID '%u' does not match shared memory id '%u'...", procID, checkID);
161 return false;
162 }
163 }
164 else {
165 if (manager->processMemory->getProcessID(name.c_str(), checkID) && checkID) {
166 procID = checkID;
167 }
168 else {
169 if (!manager->processMemory->createNewProcess(name.c_str(), procID)) {
170 LogPrint(0, LOG_SPACE, 0, "PsySpace couldn't connect to local Node %u...", systemID);
171 return false;
172 }
173 }
174 }
175
176 // Send message on sysQID to inform node that process has started
178 if (!manager->processMemory->addToCmdQ(0, msg)) {
179 LogPrint(procID, LOG_SPACE, 0, "Could not communicate with System Command Queue...");
180 delete(msg);
181 return false;
182 }
183 delete(msg);
184
185 //if (!manager->processMemory->createNewProcess(name.c_str(), procID)) {
186 // LogPrint(0, 0, 0, "Could not create Process Map\n");
187 // return false;
188 //}
189
190 if (cmdline)
191 manager->processMemory->setProcessCommandLine(procID, cmdline);
192 manager->processMemory->setProcessStatus(procID, PSYPROC_INIT);
193
194 // Connect to Node
195 // Send message on sysQID to inform node that process has started
197 if (!manager->processMemory->addToCmdQ(0, msg)) {
198 LogPrint(procID, LOG_SPACE, 0, "Could not communicate with System Command Queue...");
199 delete(msg);
200 return false;
201 }
202 delete(msg);
203
204 if (isMaster) {
205 // Logging will be done via LogPrint directly to the Node as we are in the same process
206 }
207 else {
208 //LogSystem::SetLogReceiver(this);
209 }
210
211 uint64 lastSeen;
212 manager->getNodeStatus(lastSeen, masterCreatedTime);
213
214 // Setup Heartbeat
215 manager->processMemory->setProcessStatus(procID, PSYPROC_READY);
216
217 // Start SpaceThread
219 LogPrint(procID, LOG_SPACE, 0, "Could not start PsySpace thread...");
220 return false;
221 }
222
223 return true;
224}
225
226bool PsySpace::isConnected(uint32 timeoutMS) {
227 if (!manager) return false;
228
229 uint64 lastSeen, createTime;
230 uint8 status = manager->getNodeStatus(lastSeen, createTime);
231 if (masterCreatedTime && (createTime != masterCreatedTime)) {
232 LogPrint(procID, LOG_SPACE, 0, "Psyclone Node process appears to have restarted...");
233 reset();
234 return false;
235 }
236 if (status != PSYCLONE_STATUS_READY) {
237 LogPrint(procID, LOG_SPACE, 0, "Psyclone Node process doesn't appear to be running...");
238 reset();
239 return false;
240 }
241 if (GetTimeAgeMS(lastSeen) > (int32)timeoutMS) {
242 LogPrint(procID, LOG_SPACE, 0, "Psyclone Node process appears to have stopped, resetting connection...");
243 reset();
244 return false;
245 }
246 return true;
247}
248
250 if (!isMaster)
252 if (manager)
253 shutdown();
254 if (manager) delete(manager);
255 manager = NULL;
256
257 std::map<std::string, utils::Library*>::iterator it = libraries.begin(), itEnd = libraries.end();
258 while (it != itEnd)
259 delete((it++)->second);
260 libraries.clear();
261
262 std::map<uint32, PsyAPI*>::iterator ait = psyAPIs.begin(), aitEnd = psyAPIs.end();
263 while (ait != aitEnd)
264 delete((ait++)->second);
265 psyAPIs.clear();
266
267 std::map<PsyType, SignalStruct*>::iterator sit = signalList.begin(), sitEnd = signalList.end();
268 while (sit != sitEnd) {
269 delete(sit->second->lastSignalMsg);
270 delete((sit++)->second);
271 }
272 signalList.clear();
273 return true;
274}
275
276bool PsySpace::start(uint16 threadCount) {
277 // Setup Threading Pool
278 LogPrint(procID, LOG_SPACE, 1, "PsySpace '%s' (%u) starting up...", name.c_str(), procID);
279 return setThreadPoolSize(threadCount);
280}
281
283 if (finishedShuttingDown)
284 return true;
285 finishedShuttingDown = true;
286 stop();
287
288 //LogPrint(procID, LOG_SPACE, 1, "Space ID %u shutting down...", procID);
289 LogPrint(procID, LOG_SPACE, 1, "PsySpace '%s' (%u) shutting down...", name.c_str(), procID);
290
292 if (!manager->processMemory->addToCmdQ(0, msg)) {
293 LogPrint(procID, LOG_SPACE, 0, "Could not communicate with System Command Queue...");
294 delete(msg);
295 return false;
296 }
297 delete(msg);
298
299 // shutdown all components
300 std::map<uint32, PsyAPI*>::iterator it, itEnd;
301 for (it=psyAPIs.begin(), itEnd=psyAPIs.end(); it != itEnd; it++)
302 it->second->stop();
303
304 uint32 maxWaitCount = 20;
305 while (threadPool.size()) {
306 utils::Sleep(50);
307 if (!(--maxWaitCount)) {
308 LogPrint(procID, LOG_SPACE, 0, "PsySpace '%s' waited for the last %u threads to terminate, killing threads now...", name.c_str(), threadPool.size());
309 std::map<uint32, uint8>::iterator threadI = threadPool.begin(), threadE = threadPool.end();
310 while (threadI != threadE) {
311 if (!ThreadManager::TerminateThread(threadI->first))
312 LogPrint(procID, LOG_SPACE, 0, "PsySpace '%s' couldn't kill thread %u...", name.c_str(), threadI->first);
313 else
314 LogPrint(procID, LOG_SPACE, 0, "PsySpace '%s' killed thread %u...", name.c_str(), threadI->first);
315 threadI++;
316 }
317 threadPool.clear();
318 break;
319 }
320 }
321 while (isRunning) {
322 LogPrint(procID, LOG_SPACE,0,"PsySpace '%s' waiting for the main thread to terminate...", name.c_str());
323 utils::Sleep(50);
324 }
325
326 manager->processMemory->setProcessStatus(procID, PSYPROC_TERMINATED);
327 LogPrint(procID, LOG_SPACE, 0, "PsySpace '%s' has shut down (%u, %p)", name.c_str(), procID, this);
328 return true;
329}
330
332 return finishedShuttingDown;
333}
334
335// ***************** PsyProbe plugins *****************
336bool PsySpace::addPsyProbeCustomView(uint32 compID, const char* name, const char* templateURL) {
338 msg->setString("Name", name);
339 msg->setString("Template", templateURL);
340 manager->processMemory->addToCmdQ(0, msg);
341 delete(msg);
342 return true;
343}
344
345
347 if (!entry)
348 return false;
349
350 DataMessage* msg = new DataMessage(CTRL_LOGPRINT, procID);
351 msg->setData("LogEntry", (char*)entry, entry->size);
352 manager->processMemory->addToCmdQ(0, msg);
353 delete(msg); // DataMessage is new'd above - scalar delete is correct
354 // free(), not delete: LogEntry is a variable-size record malloc'd by its
355 // callers (PsyAPI::logPrint:1238, LogSystemPrint in Utils.cpp:335). Releasing
356 // it with operator delete is an alloc-dealloc mismatch - undefined behaviour.
357 // The two allocators sit on adjacent lines here, which is how it was missed.
358 free(entry);
359 return true;
360}
361
363 return procID;
364}
365
366bool PsySpace::setThreadPoolSize(uint16 threadCount) {
367
368 uint32 id;
369 int32 n;
370 int32 c = threadCount - (uint32)threadPool.size();
371 threadTarget = threadCount;
372// LogPrint(procID, LOG_SPACE,0,"Setting PsySpace thread pool size to %u...", threadTarget);
373 lastThreadPoolCheck = GetTimeNow();
374 if (c > 0) {
375 LogPrint(procID, LOG_SPACE,2,"Creating %u more Pool Threads [%u -> %u]...",
376 c, (uint32)threadPool.size(), threadCount);
377 // create c more threads
378 for (n=0; n<c; n++) {
380 threadPool[id] = 1;
381 else
382 return false;
383 }
384 }
385 return true;
386}
387
388PsyAPI* PsySpace::getCrankAPI(const char* name) {
389 PsyAPI* api;
390 uint16 crankID;
391 if (!manager->dataMapsMemory->getCrankID(name, crankID) || !crankID)
392 return NULL;
393
394 uint32 compID = manager->dataMapsMemory->getCrankCompID(crankID);
395 if (!compID)
396 return NULL;
397
398 uint16 oldCompNodeID = manager->componentMemory->getComponentNodeID(compID);
399 if (!oldCompNodeID)
400 return NULL;
401
402 if (!pullRemoteComponentData(compID, oldCompNodeID))
403 return NULL;
404
405 if (!threadPoolMutex.enter(1000, __FUNCTION__))
406 return NULL;
407 if (!(api = psyAPIs[crankID])) {
408 api = new PsyAPI(this);
410 api->currentCompID = compID;
411 psyAPIs[crankID] = api;
412 }
413 threadPoolMutex.leave();
414 return api;
415}
416
417bool PsySpace::registerCrankCallback(const char* name, CrankFunction func) {
418 uint16 crankID;
419 if (!manager->dataMapsMemory->getCrankID(name, crankID) || !crankID)
420 return false;
421 if (!threadPoolMutex.enter(1000, __FUNCTION__))
422 return false;
423 cranks[crankID] = func;
424 threadPoolMutex.leave();
425 return true;
426}
427
428bool PsySpace::registerInternalCrank(const char* name, CrankFunction func) {
429 if (!name || !*name || !func)
430 return false;
431 if (!threadPoolMutex.enter(1000, __FUNCTION__))
432 return false;
433 internalCranks[name] = func;
434 threadPoolMutex.leave();
435 return true;
436}
437
438uint32 PsySpace::getComponentID(const char* name) {
439 uint32 compID;
440 if (manager->componentMemory->getComponentID(name, compID))
441 return compID;
442 else
443 return 0;
444}
445
446bool PsySpace::pullRemoteComponentData(uint32 compID, uint16 fromNodeID) {
448 msg->setInt("CompID", compID);
449 msg->setInt("ProcID", this->procID);
450 msg->setInt("NodeID", fromNodeID);
451 DataMessage* resultMsg = NULL;
452 uint8 result = query(msg, &resultMsg, 5000);
453 if (resultMsg)
454 delete(resultMsg);
455 delete(msg);
456 return (result >= QUERY_SUCCESS);
457}
458
459
460//PsyAPI* PsySpace::getComponentAPI(const char* name) {
461// return getComponentAPI(getComponentID(name));
462//}
463
464//PsyAPI* PsySpace::getComponentAPI(uint32 compID) {
465// if (!compID)
466// return false;
467//}
468
469//bool PsySpace::registerComponentCallback(const char* name, CrankFunction func) {
470// return registerComponentCallback(getComponentID(name), func);
471//}
472
473//bool PsySpace::registerComponentCallback(uint32 compID, CrankFunction func) {
474// if (!compID)
475// return false;
476//}
477
478
480 // Add datamessage to memory
481 uint64 memID = 0;
482 uint64 eol = msg->getEOL();
483 if (eol && (eol < msg->getCreatedTime() - 10000))
484 return false;
485 if (eol) {
486 if (!manager->insertMessage(msg, memID)) {
487 LogPrint(procID, LOG_SPACE, 0, "Could not add message from %u to shared memory...", msg->getFrom());
488 return false;
489 }
490 msg->data->memid = memID;
491 }
492 if (!manager->processMemory->addToMsgQ(0, msg)) {
493 LogPrint(procID, LOG_SPACE, 0, "Could not communicate with System Message Queue...");
494 return false;
495 }
496 manager->processMemory->addToProcessStats(procID, NULL, msg);
497// printf("************ Space Post Msg %lld us old...\n", GetTimeAge(msg->getCreatedTime()));
498 return true;
499}
500
501
502// ***************** Signals *****************
503bool PsySpace::emitSignal(const PsyType& type, DataMessage* msg) {
504 // Send signal to local signal struct first
505 if (!signalsMutex.enter(3000, __FUNCTION__))
506 return false;
507 SignalStruct* signal = signalList[type];
508 if (!signal) {
509 signal = new SignalStruct;
510 signal->lastSignalMsg = NULL;
511 signalList[type] = signal;
512 }
513 signalsMutex.leave();
514
515 if (signal->mutex.enter(3000, __FUNCTION__)) {
516 if (signal->lastSignalMsg)
517 delete(signal->lastSignalMsg);
518 signal->lastSignalMsg = msg;
519 signal->mutex.leave();
520 signal->event.signal();
521 }
522 else {
523 return false;
524 }
525 // Then to all other procs and the other nodes (via Node proc 0)
526// if (!manager->processMemory->addToAllSignalQs(msg)) {
527 if (!manager->processMemory->addToAllSignalQsExcept(msg, procID)) {
528 LogPrint(procID, LOG_SPACE, 0, "Could not communicate with All Signal Queues...");
529 return false;
530 }
531 manager->processMemory->addToProcessStats(procID, NULL, msg);
532 return true;
533}
534
535DataMessage* PsySpace::waitForSignal(const PsyType& type, uint32 timeout, uint64 lastReceivedTime) {
536
537 if (!signalsMutex.enter(3000, __FUNCTION__))
538 return NULL;
539 SignalStruct* signal = signalList[type];
540 if (!signal) {
541 signal = new SignalStruct;
542 signal->lastSignalMsg = NULL;
543 signalList[type] = signal;
544 }
545 signalsMutex.leave();
546
547 DataMessage* msg = NULL;
548
549 if (lastReceivedTime) {
550 if (signal->mutex.enter(3000, __FUNCTION__)) {
551 if (signal->lastSignalMsg && (signal->lastSignalMsg->getCreatedTime() > lastReceivedTime)) {
552 msg = new DataMessage((char*)signal->lastSignalMsg->data, true);
553 // printf("************ Space NoWait Signal %llu us old...\n", GetTimeAge(msg->getCreatedTime()));
554 signal->mutex.leave();
555 //LogPrint(procID, LOG_SPACE, 0, "Space signal no wait success %s...", PrintTimeString(msg->getCreatedTime()).c_str());
556 manager->processMemory->addToProcessStats(procID, msg, NULL);
557 return msg;
558 }
559 else {
560 //LogPrint(procID, LOG_SPACE, 0, "Not newer %s >= %s", PrintTimeString(lastReceivedTime).c_str(),
561 // PrintTimeString(signal->lastSignalMsg->getCreatedTime()).c_str());
562 //if (lastReceivedTime > signal->lastSignalMsg->getCreatedTime())
563 // LogPrint(procID, LOG_SPACE, 0, "%llu > %llu", lastReceivedTime, signal->lastSignalMsg->getCreatedTime());
564 //else if (lastReceivedTime < signal->lastSignalMsg->getCreatedTime())
565 // LogPrint(procID, LOG_SPACE, 0, "%llu < %llu", lastReceivedTime, signal->lastSignalMsg->getCreatedTime());
566 signal->mutex.leave();
567 }
568 }
569 }
570
571 //if (signal->lastSignalMsg)
572 // LogPrint(procID, LOG_SPACE, 0, "Space signal wait (%s)...", PrintTimeString(signal->lastSignalMsg->getCreatedTime()).c_str());
573 //else
574 // LogPrint(procID, LOG_SPACE, 0, "Space signal wait...");
575
576 if (signal->event.waitNext(timeout)) {
577 //LogPrint(procID, LOG_SPACE, 0, "Space signal wait success...");
578 if (signal->mutex.enter(3000, __FUNCTION__) && signal->lastSignalMsg) {
579 if (lastReceivedTime) {
580 if (signal->lastSignalMsg->getCreatedTime() > lastReceivedTime)
581 msg = new DataMessage((char*)signal->lastSignalMsg->data, true);
582 }
583 else
584 msg = new DataMessage((char*)signal->lastSignalMsg->data, true);
585 //if (msg)
586 //printf("************ Space Wait Signal %llu us old...\n", GetTimeAge(msg->getCreatedTime()));
587 // LogPrint(procID, LOG_SPACE, 0, "Space signal wait success %s...", PrintTimeString(msg->getCreatedTime()).c_str());
588 }
589 signal->mutex.leave();
590 }
591
592 if (msg)
593 manager->processMemory->addToProcessStats(procID, msg, NULL);
594 return msg;
595}
596
597
598uint8 PsySpace::query(DataMessage* msg, DataMessage** result, uint32 timeout) {
599 *result = NULL;
600 if (!msg)
601 return QUERY_FAILED;
602
603 uint32 to = msg->getTo();
604
605 uint32 reqID;
606 if (!manager->dataMapsMemory->createNewRequest(msg->getFrom(), to, 0, reqID))
607 return QUERY_FAILED;
608// LogPrint(procID, LOG_SPACE,0,"Created Request id %u from comp %u...", reqID, msg->getFrom());
609
610 msg->setReference(reqID);
611 manager->processMemory->addToProcessStats(procID, NULL, msg);
612
613 uint16 otherNodeID = 0;
614 uint16 otherProcID = 0;
615 // If to is 0 this request is for the local Node
616 if (!to) {
617 manager->dataMapsMemory->setRequestStatus(reqID, REQ_PROCESSING_LOCAL);
618 if (!manager->processMemory->addToReqQ(0, msg)) {
619 manager->dataMapsMemory->deleteRequest(reqID);
620 return QUERY_FAILED;
621 }
622 }
623 else {
624 if (!manager->componentMemory->getComponentLocation(to, otherNodeID, otherProcID))
625 return QUERY_FAILED;
626
627 if (otherNodeID != manager->getNodeID()) {
628 manager->dataMapsMemory->setRequestStatus(reqID, REQ_PROCESSING_REMOTE);
629 if (!manager->processMemory->addToReqQ(0, msg)) {
630 manager->dataMapsMemory->deleteRequest(reqID);
631 return QUERY_FAILED;
632 }
633 }
634 else {
635 manager->dataMapsMemory->setRequestStatus(reqID, REQ_PROCESSING_LOCAL);
636 otherProcID = manager->componentMemory->getComponentProcessID(to);
637 if (!manager->processMemory->addToReqQ(otherProcID, msg)) {
638 manager->dataMapsMemory->deleteRequest(reqID);
639 return QUERY_FAILED;
640 }
641 }
642 }
643
644 uint8 status = 0;
645 uint64 time = 0;
646 uint64 msgID = 0;
647 uint64 msgEOL = 0;
648 if (!manager->dataMapsMemory->waitForRequestReply(reqID, timeout, status, time, msgID, msgEOL)) {
649 LogPrint(procID, LOG_SPACE,0,"Request reply timeout (%u) from node %u (req %u from comp %u)...",
650 status, otherNodeID, reqID, msg->getFrom());
651 manager->dataMapsMemory->deleteRequest(reqID);
652 return QUERY_TIMEOUT;
653 }
654 manager->dataMapsMemory->deleteRequest(reqID);
655
656 if (status < REQ_SUCCESS) {
657 if (status >= REQ_FAILED)
658 return QUERY_FAILED;
659 if (status == REQ_FAILED_TO_SEND)
660 LogPrint(procID, LOG_SPACE, 0, "Request failed as remote system cannot be reached (req %u from comp %u)...", reqID, msg->getFrom());
661 else
662 LogPrint(procID, LOG_SPACE,0,"Request reply failed (%u) from node %u (req %u from comp %u)...", status, otherNodeID, reqID, msg->getFrom());
663 return QUERY_NOT_REACHABLE;
664 }
665
666 if (!msgID)
667 return QUERY_SUCCESS;
668
669 if (!(*result = manager->temporalMemory->getCopyOfMessage(msgID))) {
670 LogPrint(procID, LOG_SPACE,0,"Couldn't getCopyOfMessage reply from node %u (req %u from comp %u)...", otherNodeID, reqID, msg->getFrom());
671 return QUERY_FAILED;
672 }
673
674 if (*result)
675 manager->processMemory->addToProcessStats(procID, *result, NULL);
676 return QUERY_SUCCESS;
677}
678
679bool PsySpace::queryReply(uint32 id, uint8 status, DataMessage* resultMsg) {
680
681 if (resultMsg)
682 manager->processMemory->addToProcessStats(procID, NULL, resultMsg);
683 // Check if this request was made locally
684 uint32 remoteID;
685 uint32 origin;
686 uint32 source;
687 if (!manager->dataMapsMemory->getRequestInfo(id, origin, source, remoteID)) {
688 LogPrint(procID, LOG_SPACE,0,"Couldn't find request info for local id %u", id);
689 return false;
690 }
691 // could this be a INTERSYSTEM_QUERY_REPLY
692 if (remoteID && !origin) {
693 DataMessage* netMsg = new DataMessage(PsyAPI::CTRL_INTERSYSTEM_QUERY_REPLY, source, origin, 10000);
694 // keep the local request id here, needed by the InterSystemManager
695 netMsg->setReference(id);
696 netMsg->setStatus(status);
697 if (resultMsg)
698 netMsg->setAttachedMessage("DataMessage", resultMsg);
699 manager->processMemory->addToReqQ(0, netMsg);
700 //LogPrint(procID, LOG_SPACE,3,"Replied to remote request %u (local %u) (source %u, origin %u)...", remoteID, id, source, origin);
701 delete(resultMsg);
702 delete(netMsg);
703 // dont delete this: manager->dataMapsMemory->deleteRequest(id);
704 // needed by the InterSystemManager
705 return true;
706 }
707 else if (remoteID) {
708 uint16 otherNodeID = manager->componentMemory->getComponentNodeID(origin);
709 if (otherNodeID && (otherNodeID != manager->getNodeID())) {
710 DataMessage* netMsg = new DataMessage(PsyAPI::CTRL_QUERY_REPLY, source, origin, 10000);
711 netMsg->setReference(remoteID);
712 netMsg->setStatus(status);
713 if (resultMsg)
714 netMsg->setAttachedMessage("DataMessage", resultMsg);
715 manager->processMemory->addToReqQ(0, netMsg);
716 //LogPrint(procID, LOG_SPACE,3,"Replied to remote request %u (local %u) (source %u, origin %u)...", remoteID, id, source, origin);
717 delete(resultMsg);
718 delete(netMsg);
719 return manager->dataMapsMemory->deleteRequest(id);
720 }
721 else {
722 LogPrint(procID, LOG_SPACE,0,"Couldn't find otherNode %u for remote request %u (local %u) (source %u, origin %u)...",
723 otherNodeID, remoteID, id, source, origin);
724 return false;
725 }
726 }
727
728 uint64 msgID = 0;
729 uint64 msgEOL = 0;
730 uint8 reqStatus = 0;
731
732 if (resultMsg) {
733 resultMsg->setTTL(10000000);
734 msgEOL = resultMsg->getEOL();
735 // decide on EOL ###################
736 manager->temporalMemory->insertMessage(resultMsg, msgID);
737 delete(resultMsg);
738 }
739
740 if (status == QUERY_SUCCESS) {
741 if (msgID)
742 reqStatus = REQ_SUCCESS_DATA_EOL;
743 else
744 reqStatus = REQ_SUCCESS;
745 }
746 else {
747 if (msgID)
748 reqStatus = REQ_FAILED_DATA_EOL;
749 else
750 reqStatus = REQ_FAILED;
751 }
752 return manager->dataMapsMemory->setRequestStatus(id, reqStatus, msgID, msgEOL);
753}
754
755
756
757
758
759
760
761
762
763
764
765bool PsySpace::threadPoolDispatch() {
766 DataMessage* msg, *dmsg;
767 uint32 threadID;
769 uint32 idleCount = 0;
770 uint8 proc;
771 char *crankName = new char[MAXVALUENAMELEN+1];
772 crankName[0] = 0;
773 char *crankFunction = new char[MAXVALUENAMELEN + 1];
774 crankFunction[0] = 0;
775 char *crankLanguage = new char[MAXVALUENAMELEN + 1];
776 crankLanguage[0] = 0;
777 char *libraryFilename = new char[MAXVALUENAMELEN + 1];
778 libraryFilename[0] = 0;
779 CrankFunction crank;
780 uint32 compID;
781 PsyAPI* api;
782 uint32 s;
783 TriggerSpec* trigger;
784
785 while (shouldContinue) {
786// printf(".");
787 threadPoolMutex.enter();
788
789// if (GetTimeAgeMS(lastThreadPoolCheck) > threadPoolCheckInterval) {
790 idleCount = waitCounters[PROCMESSAGE] + waitCounters[PROCSIGNAL] + waitCounters[PROCREQUEST];
791 // idleCount = waitCounters[PROCMESSAGE] + waitCounters[PROCSIGNAL];
792 if (idleCount < threadPoolWaitLowerThreshold)
793 setThreadPoolSize((uint16)(threadPool.size() + threadPoolWaitIncrement));
794 // else if (idleCount > threadPoolWaitLowerThreshold)
795 // setThreadPoolSize(threadPool.size() - threadPoolWaitIncrement);
796 else
797 lastThreadPoolCheck = GetTimeNow();
798// }
799
800 if (threadTarget < threadPool.size()) {
801 threadPoolMutex.leave();
802 break;
803 }
804
805 // uint64 t = GetTimeNow();
806 if ( (waitCounters[PROCMESSAGE] <= waitCounters[PROCSIGNAL]) && (waitCounters[PROCMESSAGE] <= waitCounters[PROCREQUEST]) ) {
807 // if (waitCounters[PROCMESSAGE] <= waitCounters[PROCSIGNAL]) {
808 proc = PROCMESSAGE;
809 waitCounters[proc]++;
810 threadPoolMutex.leave();
811 msg = manager->processMemory->waitForMsgQ(procID, 100);
812 // if (msg)
813 // printf("Wait for Msg counter: %llu [%u / %u]\n", GetTimeAge(msg->getCreatedTime()), waitCounters[PROCMESSAGE], waitCounters[PROCSIGNAL]);
814 // printf("************ Space waited %llu...\n", GetTimeAge(t));
815 }
816 else if ( (waitCounters[PROCREQUEST] <= waitCounters[PROCMESSAGE]) && (waitCounters[PROCREQUEST] <= waitCounters[PROCSIGNAL]) ) {
817 proc = PROCREQUEST;
818 waitCounters[proc]++;
819 threadPoolMutex.leave();
820 msg = manager->processMemory->waitForReqQ(procID, 100);
821 //if (msg) LogPrint(procID, LOG_SPACE, 1, "Got request message, processing...");
822 }
823 else {
824 proc = PROCSIGNAL;
825 waitCounters[proc]++;
826 threadPoolMutex.leave();
827 if (msg = manager->processMemory->waitForSigQ(procID, 100)) {
828 manager->processMemory->addToProcessStats(procID, msg, NULL);
829 // printf("Wait for Signal counter: %u [%u / %u]\n", GetTimeAge(msg->getCreatedTime()), waitCounters[PROCMESSAGE], waitCounters[PROCSIGNAL]);
830 if (signalsMutex.enter(3000, __FUNCTION__)) {
831 SignalStruct* signal = signalList[msg->getType()];
832 if (!signal) {
833 signal = new SignalStruct;
834 signal->lastSignalMsg = NULL;
835 signalList[msg->getType()] = signal;
836 }
837 signalsMutex.leave();
838 if (signal->mutex.enter(3000, __FUNCTION__)) {
839 if (signal->lastSignalMsg)
840 delete(signal->lastSignalMsg);
841 signal->lastSignalMsg = msg;
842 signal->mutex.leave();
843 signal->event.signal();
844 LogPrint(procID, LOG_SPACE, 5, "Space signal signal %s...", PrintTimeString(msg->getCreatedTime()).c_str());
845 }
846 else
847 delete(msg);
848 }
849 else
850 delete(msg);
851 msg = NULL;
852 }
853 }
854 //else {
855 // proc = PROCTIMER;
856 // waitCounters[proc]++;
857 // threadPoolMutex.leave();
858 // msg = timeQ->waitForNextEvent(30);
859 //}
860 while (!threadPoolMutex.enter(1000, "Threadpool run with msg")) {
861 }
862
863 waitCounters[proc]--;
864 procCounters[proc]++;
865
866 uint16 crankID;
867 char* compName;
868 if (msg) {
869 crank = NULL;
870 crankID = 0;
871 dmsg = NULL;
872
873// printf("************ Space Deliver to %u (%lld us old) %s...\n",
874// msg->getTo(), GetTimeAge(msg->getCreatedTime()), PrintTimeString(msg->getCreatedTime()).c_str());
875 // ⚠️ BakeLive Anomaly (see BakeLiveBug.md): the four `delete(msg); continue;`
876 // paths below each discard a message that was already accepted into the
877 // component's shared queue, and they log at level 0/1 which the unit-test
878 // framework suppresses. A message lost here is therefore invisible to every
879 // instrument on either side of the process boundary.
880 // These paths were fully traced during that investigation and measured
881 // 0/0/0/0 on failing runs - they are NOT the anomaly - but keep this note:
882 // anyone hunting silent local-delivery loss should instrument here first.
883 if (!(compID = msg->getTo())) {
884 LogPrint(procID, LOG_SPACE, 1, "No compID, deleting...");
885 delete(msg);
886 procCounters[proc]--;
887 threadPoolMutex.leave();
888 continue;
889 }
890
891 manager->processMemory->addToProcessStats(procID, msg, NULL);
892
894 // T1.6: a CTRL_TRIGGER_GROUP joined activation carries a TriggerSpec and
895 // a primary "Message" just like CTRL_TRIGGER (plus a "Members" map read
896 // by waitForNewMessageGroup()); dispatch it through the same crank path.
897 if (!(trigger = (TriggerSpec*) msg->getData("TriggerSpec", s))) {
898 delete(msg);
899 procCounters[proc]--;
900 threadPoolMutex.leave();
901 continue;
902 }
903 crankID = trigger->crankID;
904 if (!crankID) {
905 //crank = internalCranks["Simple"];
906 LogPrint(procID, LOG_SPACE, 0, "Crank not found for component!");
907 delete (msg);
908 procCounters[proc]--;
909 threadPoolMutex.leave();
910 continue;
911 }
912
913 // Get actual data message (primary; may be absent for an empty-tag group)
914 if (!(dmsg = msg->getAttachedMessageCopy("Message"))) {
915 if (msg->getType() == PsyAPI::CTRL_TRIGGER) {
916 LogPrint(procID, LOG_SPACE, 0, "Couldn't find attached input message");
917 delete (msg);
918 procCounters[proc]--;
919 threadPoolMutex.leave();
920 continue;
921 }
922 // CTRL_TRIGGER_GROUP without a primary message is still valid: the
923 // crank consumes the member map via waitForNewMessageGroup().
924 }
925 }
926 else {
927 //LogPrint(procID, LOG_SPACE, 1, "Looking for crank for direct message to component queue");
928 if (!(crankID = compCrankIDs[compID])) {
929 compName = new char[MAXKEYNAMELEN+1];
930 if (manager->componentMemory->getComponentName(compID, compName, MAXKEYNAMELEN)) {
931 utils::strcpyavail(compName+strlen(compName), "ComponentCrank", 20, true);
932 if (manager->dataMapsMemory->getCrankID(compName, crankID)) {
933 compCrankIDs[compID] = crankID;
934 }
935 }
936 else
937 compName[0] = 0;
938 if (!crankID) {
939 LogPrint(procID, LOG_SPACE, 0, "Unknown Component Crank for '%s' (%u)", compName, compID);
940 delete [] compName;
941 delete (msg);
942 procCounters[proc]--;
943 threadPoolMutex.leave();
944 continue;
945 }
946 delete [] compName;
947 }
948 }
949
950 // Load function from already loaded cranks
951 if (!crank && !(crank = cranks[crankID])) {
952 manager->dataMapsMemory->getCrankName(crankID, crankName, 256);
953 // Check if this is a scripted module
954 if (manager->dataMapsMemory->getCrankLanguage(crankID, crankLanguage, MAXVALUENAMELEN) && crankLanguage && strlen(crankLanguage)) {
955 utils::StringFormatInto(crankFunction, MAXVALUENAMELEN, "%sLink", utils::TextCapitalise(crankLanguage).c_str());
956 utils::StringFormatInto(libraryFilename, MAXVALUENAMELEN, "Psy%sLink", utils::TextCapitalise(crankLanguage).c_str());
957 LogPrint(procID, LOG_SPACE, 0, "Scripted language (%s) crank '%s' loading binary library '%s'", crankLanguage, crankName, libraryFilename);
958 }
959 else {
960 // Load function from library
961 if (!manager->dataMapsMemory->getCrankFunction(crankID, crankFunction, MAXVALUENAMELEN) || !crankFunction) {
962 LogPrint(procID, LOG_SPACE, 0, "Unknown Crank ID %u in Trigger Message", crankID);
963 delete (msg);
964 procCounters[proc]--;
965 threadPoolMutex.leave();
966 continue;
967 }
968 if (!manager->dataMapsMemory->getCrankLibraryFilename(crankID, libraryFilename, MAXVALUENAMELEN) || !libraryFilename) {
969 LogPrint(procID, LOG_SPACE, 0, "Crank '%s' doesn't have a valid library specified", crankFunction);
970 delete (msg);
971 procCounters[proc]--;
972 threadPoolMutex.leave();
973 continue;
974 }
975 }
976
977 // If crankFunction and/or libraryFilename are not yet set, accept
978 // as the message will be added to the API queue for when the module
979 // gets instantiated
980
981 if (crankFunction && strlen(crankFunction)) {
982 if (!(crank = loadCrankFromLibrary(crankFunction, libraryFilename))) {
983 if (strlen(crankLanguage)) {
984 LogPrint(procID, LOG_SPACE, 0, "The language library '%s' is unavailable - the language name '%s' may be incorrect", libraryFilename, crankLanguage);
985 }
986 else
987 LogPrint(procID, LOG_SPACE, 0, "Couldn't find crank '%s' in library '%s'", crankFunction, libraryFilename);
988 delete (msg);
989 procCounters[proc]--;
990 threadPoolMutex.leave();
991 continue;
992 }
993 cranks[crankID] = crank;
994 }
995 }
996
997 //printf("************ Space Trigger %llu us old, Msg %llu us old...\n", GetTimeAge(msg->getCreatedTime()), GetTimeAge(dmsg->getCreatedTime()));
998
999 // Is the crank already running
1000 if (!(api = psyAPIs[crankID])) {
1001 api = new PsyAPI(this);
1003 psyAPIs[crankID] = api;
1004 }
1005
1006 // ⚠️ The wrapper (msg) is a CTRL_TRIGGER envelope with its OWN serial, while the
1007 // subscriber reads getSerial() off the ATTACHED PAYLOAD (dmsg). Any future
1008 // correlation of a queued entry back to the message it carries must use the
1009 // payload's serial, not the wrapper's - conflating the two produced a
1010 // confidently wrong conclusion during the BakeLive Anomaly investigation.
1011 uint8 status;
1012 if (dmsg)
1013 status = api->addInputTrigger(crankID, compID, msg, dmsg);
1014 else if (msg->getType() == PsyAPI::CTRL_TRIGGER_GROUP)
1015 // T1.6: a group activation with no primary (e.g. empty-tag grouping)
1016 // still delivers as a trigger so waitForNewMessageGroup() sees a fresh
1017 // trigger name and reads the "Members" map off the wrapper.
1018 status = api->addInputTrigger(crankID, compID, msg, NULL);
1019 else {
1020 //LogPrint(procID, LOG_SPACE, 1, "Added direct message to component queue");
1021 status = api->addInputTrigger(crankID, compID, NULL, msg);
1022 }
1023 switch(status) {
1024 case CRANKAPI_RUNNING:
1025 procCounters[proc]--;
1026 threadPoolMutex.leave();
1027 continue;
1028 case CRANKAPI_IDLE:
1029 // threadPoolMutex.leave();
1030 break;
1031 case CRANKAPI_INUSE:
1032 // A DIFFERENT crank is mid-execution on this PsyAPI, so
1033 // addInputTrigger did NOT queue the message (PsyAPI.cpp:353-356).
1034 // This used to fall into `default:` alongside CRANKAPI_FAILED and be
1035 // DELETED - silently destroying an already-delivered message.
1036 //
1037 // ⚠️ Re-queueing it here would BUSY-LOOP: PsyAPI::begin() sets
1038 // startedRunning for the whole crank execution and only finish()
1039 // clears it (PsyAPI.cpp:267/287), so this dispatch thread would pull
1040 // the message straight back off the queue, get INUSE again, and spin
1041 // hot for the entire duration of the other crank - which is worse
1042 // than the drop it replaces. So: re-queue, but yield first so the
1043 // running crank makes progress and the queue is not hammered.
1044 // The re-queue can itself fail (queue full / no growth); if it does we
1045 // ARE dropping, so say so loudly and count it rather than pretend.
1046 {
1047 DataMessage* requeue = dmsg ? dmsg : msg;
1048 DataMessage* discard = dmsg ? msg : NULL;
1049 // Release exactly once, like every other exit from this switch,
1050 // then do the re-queue OUTSIDE the pool lock (addToMsgQ takes the
1051 // process-memory mutex; nesting the two here would invert the
1052 // lock order used elsewhere in this function).
1053 procCounters[proc]--;
1054 threadPoolMutex.leave();
1055 utils::Sleep(1); // yield: do not spin on a busy crank
1056 if (!manager->processMemory->addToMsgQ(procID, requeue)) {
1057 LogPrint(procID, LOG_SPACE, 0, "Crank API busy for crank %u and "
1058 "re-queue FAILED; message DROPPED", crankID);
1059 delete(requeue);
1060 }
1061 delete(discard); // trigger wrapper is ours either way (NULL-safe)
1062 }
1063 continue;
1064 case CRANKAPI_FAILED:
1065 default:
1066 LogPrint(procID, LOG_SPACE, 0, "Couldn't add trigger to Crank API");
1067 delete(msg);
1068 delete(dmsg);
1069 procCounters[proc]--;
1070 threadPoolMutex.leave();
1071 continue;
1072 }
1073
1074 threadPoolMutex.leave();
1075 // Call function
1076 if (crank) {
1077 api->begin();
1078 do {
1079 try {
1080 if (crank(api) < 0)
1081 break;
1082 }
1083 catch (...) {
1084 LogPrint(procID, LOG_SPACE, 0, "Crank %u ('%s') caused an exception", crankID, crankName);
1085 break;
1086 }
1087 } while (api->shouldContinue() && api->getInputQueueSize());
1088 api->finish();
1089 }
1090 threadPoolMutex.enter();
1091 procCounters[proc]--;
1092 }
1093 threadPoolMutex.leave();
1094
1095 }
1096
1097 LogPrint(procID, LOG_SPACE,3,"Thread Pool %u exited, target %u, size: %u", threadID, threadTarget, threadPool.size() - 1);
1098 threadPoolMutex.enter();
1099 std::map<uint32, uint8>::iterator i = threadPool.find(threadID);
1100 if (i != threadPool.end())
1101 threadPool.erase(i);
1102 threadPoolMutex.leave();
1103 delete [] crankName;
1104 delete [] crankFunction;
1105 delete [] libraryFilename;
1106 return true;
1107}
1108
1109CrankFunction PsySpace::loadCrankFromLibrary(const char* crankName, const char* libraryFilename) {
1110 if (!crankName || !libraryFilename)
1111 return NULL;
1112
1113 if (!strlen(libraryFilename)) {
1114 // Internal cranks
1115 return internalCranks[crankName];
1116 }
1117
1118 utils::Library* lib = libraries[libraryFilename];
1119 if (!lib) {
1120 if (!(lib = utils::OpenLibrary(libraryFilename))) {
1121 LogPrint(procID, LOG_SPACE, 0, "Could not find or load library '%s'", libraryFilename);
1122 return NULL;
1123 }
1124 libraries[libraryFilename] = lib;
1125 }
1126
1127 CrankFunction crank = (CrankFunction)lib->getFunction(crankName);
1128 if (!crank) {
1129 LogPrint(procID, LOG_SPACE, 0, "Could not load Crank '%s' from library '%s'", crankName, libraryFilename);
1130 return NULL;
1131 }
1132 return crank;
1133}
1134
1135
1136bool PsySpace::startContinuousComponent(uint32 compID) {
1137
1138 std::map<uint32, uint32>::iterator it = continuousComponentThreads.find(compID);
1139 if (it != continuousComponentThreads.end())
1140 return false;
1141
1142 continuousComponentThreads[threadID] = 0;
1143
1144 uint32 threadID;
1146 return false;
1147
1148 continuousComponentThreads[threadID] = compID;
1149 return true;
1150}
1151
1152bool PsySpace::runContinuousComponent() {
1153 uint32 threadID;
1154 DataMessage* msg = NULL;
1155
1157
1158 uint32 count = 0;
1159 uint32 compID;
1160 while (!(compID = continuousComponentThreads[threadID])) {
1161 if (++count > 10)
1162 return false;
1163 utils::Sleep(50);
1164 }
1165
1166 // Find function name and library to call
1167 // Load function from library
1168 // Check stats
1169 // Call function
1170 // Record stats usage
1171
1172 return true;
1173}
1174
1175
1176bool PsySpace::run() {
1177 // Space Thread
1178 // Update Heartbeat
1179 manager->processMemory->setProcessStatus(procID, PSYPROC_ACTIVE);
1180 // Update Space Stats
1181 uint64 currentCPUTicks;
1182// utils::GetProcessCPUTicks(currentCPUTicks);
1183 uint32 checkCount = 50, c = 0;
1184 uint64 lastMsgCheck;
1185 std::map<uint32, PsyAPI*>::iterator i, e = psyAPIs.end();
1186
1187 DataMessage* cmsg;
1188 while (shouldContinue) {
1189 if (cmsg = manager->processMemory->waitForCmdQ(procID, 100)) {
1190 if (cmsg->getType() == PsyAPI::CTRL_SYSTEM_SHUTDOWN) {
1191 delete(cmsg);
1192 shutdown();
1193 break;
1194 }
1195 delete(cmsg);
1196 }
1197 // Update Heartbeat
1198 utils::GetProcessCPUTicks(currentCPUTicks);
1199 manager->processMemory->setProcessStatus(procID, PSYPROC_ACTIVE, currentCPUTicks);
1200
1201 if (++c > 50) {
1202 // check all apis for checkLastWaitForMessage()
1203 i = psyAPIs.begin();
1204 while (i != e) {
1205 if ((i->second) && (lastMsgCheck = i->second->checkLastWaitForMessage())) {
1206 i->second->logPrint(1, "Component has unchecked messages, last check %s ago",
1207 PrintTimeDifString(GetTimeAge(lastMsgCheck)).c_str());
1208 }
1209 i++;
1210 }
1211 c = 0;
1212 }
1213 }
1214
1215 manager->processMemory->setProcessStatus(procID, PSYPROC_SHUTTING_DOWN);
1216 //LogPrint(procID, LOG_SPACE, 1, "PsySpace '%s' shutting down...", name.c_str());
1217 isRunning = false;
1218 return true;
1219}
1220
1222 if (arg == NULL) thread_ret_val(1);
1223 thread_ret_val((int)(((PsySpace*)arg)->run() ? 0 : 1));
1224}
1225
1226
1228 if (arg == NULL) thread_ret_val(1);
1229 thread_ret_val((int)(((PsySpace*)arg)->threadPoolDispatch() ? 0 : 1));
1230}
1231
1233 if (arg == NULL) thread_ret_val(1);
1234 thread_ret_val((int)(((PsySpace*)arg)->runContinuousComponent() ? 0 : 1));
1235}
1236
1237
1238} // namespace cmlabs
#define REQ_PROCESSING_REMOTE
#define REQ_SUCCESS_DATA_EOL
#define REQ_SUCCESS
#define REQ_FAILED_DATA_EOL
#define REQ_FAILED
#define REQ_FAILED_TO_SEND
#define REQ_PROCESSING_LOCAL
#define PSYCLONE_STATUS_READY
Instance is fully operational.
#define PSYPROC_INIT
Process is initialising.
#define PSYPROC_ACTIVE
Process is actively running.
#define PSYPROC_SHUTTING_DOWN
Process is shutting down.
#define PSYPROC_TERMINATED
Process has terminated.
#define PSYPROC_READY
Process is ready to run components.
#define QUERY_NOT_REACHABLE
The target could not be reached (e.g.
Definition PsyAPI.h:62
#define QUERY_SUCCESS
The query succeeded.
Definition PsyAPI.h:60
#define CRANKAPI_RUNNING
The crank is currently executing.
Definition PsyAPI.h:40
#define QUERY_FAILED
General failure.
Definition PsyAPI.h:55
#define CRANKAPI_IDLE
The crank is idle, waiting for input.
Definition PsyAPI.h:41
#define CRANKAPI_FAILED
The API could not be obtained or is invalid.
Definition PsyAPI.h:38
#define CRANKAPI_INUSE
The API is already in use by another thread.
Definition PsyAPI.h:39
#define QUERY_TIMEOUT
No reply within the timeout.
Definition PsyAPI.h:56
The PsySpace process/node context: the runtime container in which Psyclone components (cranks) live a...
#define PROCSIGNAL
Signal processing.
Definition PsySpace.h:141
#define LOG_SPACE
Log source id used by PsySpace when writing entries to the system log.
Definition PsySpace.h:52
#define PROCREQUEST
Query/request processing.
Definition PsySpace.h:142
#define PROCMESSAGE
Ordinary trigger-message processing.
Definition PsySpace.h:140
#define MAXVALUINT32
Definition Types.h:87
#define MAXKEYNAMELEN
Definition Utils.h:85
#define thread_ret_val(ret)
Definition Utils.h:131
#define THREAD_RET
Definition Utils.h:127
#define THREAD_FUNCTION_CALL
Definition Utils.h:129
#define LogPrint
Definition Utils.h:313
#define MAXVALUENAMELEN
Definition Utils.h:86
#define THREAD_ARG
Definition Utils.h:130
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setTTL(uint64 ttl)
setTTL(uint64 ttl)
uint32 getFrom()
getFrom() Get the sender id
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
bool setTag(uint32 tag)
setTag(uint32 tag)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
bool setStatus(uint16 status)
setStatus(uint16 status)
PsyType getType()
getType()
const char * getData(const char *key, uint32 &size)
getData(const char* key, uint32& size)
bool setAttachedMessage(const char *key, DataMessage *msg)
setAttachedMessage(const char* key, DataMessage* msg)
bool setReference(uint64 ref)
setReference(uint64 ref) Set message reference
DataMessage * getAttachedMessageCopy(const char *key)
getAttachedMessageCopy(const char* key)
uint64 getEOL()
getEOL()
uint64 getCreatedTime()
getCreatedTime()
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
uint32 getTo()
getTo()
static bool SetLogReceiver(LogReceiver *rec)
Register a receiver that gets every accepted LogEntry.
Definition Utils.cpp:182
Top-level facade of the shared-memory subsystem for one process.
ProcessMemory * processMemory
Accessor for the process table and per-process queues.
DataMessage * waitForMsgQ(uint16 procID, uint32 timeout)
Wait on the data-message queue.
The API handle a component (crank) uses to talk to the Psyclone system.
Definition PsyAPI.h:82
static struct PsyType CTRL_PROCESS_INITIALISE
Sent to a process to initialise it.
Definition PsyAPI.h:91
static struct PsyType CTRL_PROCESS_SHUTDOWN
Orders a single process to shut down.
Definition PsyAPI.h:93
static struct PsyType CTRL_PULLCOMPONENTDATA
Requests component data from another node.
Definition PsyAPI.h:101
static struct PsyType CTRL_TRIGGER_GROUP
T1.6: wraps a <triggergroup> joined-set delivery (member map) to a component.
Definition PsyAPI.h:98
static struct PsyType CTRL_TRIGGER
Wraps a trigger delivery to a component.
Definition PsyAPI.h:97
static struct PsyType CTRL_PROCESS_GREETING
Handshake from a newly joined process.
Definition PsyAPI.h:92
bool shouldContinue()
Check whether the crank should keep running, or exit its loop.
Definition PsyAPI.cpp:93
static struct PsyType CTRL_QUERY_REPLY
Carries a query reply back to the asker.
Definition PsyAPI.h:100
bool setCommandlineBasedir(const char *cmdlineBasedir)
Internal use only.
Definition PsyAPI.cpp:110
uint32 getInputQueueSize()
Get the size of the input queue, i.e.
Definition PsyAPI.cpp:245
static struct PsyType CTRL_INTERSYSTEM_QUERY_REPLY
Reply from a different Psyclone system.
Definition PsyAPI.h:106
static struct PsyType CTRL_SYSTEM_SHUTDOWN
Orders a full system shutdown.
Definition PsyAPI.h:95
static struct PsyType CTRL_CREATECUSTOMPAGE
Registers a custom PsyProbe view.
Definition PsyAPI.h:102
bool postMessage(DataMessage *msg)
Post a raw message into the system for distribution to subscribers.
Definition PsySpace.cpp:479
bool reset()
Reset the space's runtime state (counters, queues) without disconnecting.
Definition PsySpace.cpp:249
bool connect(uint16 systemID, bool isMaster=false, const char *cmdline=NULL)
Join a Psyclone system (attach to the node's shared memory).
Definition PsySpace.cpp:134
bool registerInternalCrank(const char *name, CrankFunction func)
Register a built-in ("internal") crank by function name, resolved when a component's <crank function=...
Definition PsySpace.cpp:428
bool shutdown()
Request an orderly shutdown of the space and all its components.
Definition PsySpace.cpp:282
friend THREAD_RET THREAD_FUNCTION_CALL PsySpaceRun(THREAD_ARG arg)
Thread entry for the space's main service thread.
bool start(uint16 threadCount=5)
Start processing: spin up the thread pool and begin dispatching triggers.
Definition PsySpace.cpp:276
uint8 query(DataMessage *msg, DataMessage **result, uint32 timeout)
Send a query message and wait for its reply.
Definition PsySpace.cpp:598
uint32 getComponentID(const char *name)
Look up the numeric component id for a component name.
Definition PsySpace.cpp:438
DataMessage * waitForSignal(const PsyType &type, uint32 timeout, uint64 lastReceivedTime=0)
Block until a signal of the given type arrives (or has already arrived).
Definition PsySpace.cpp:535
friend THREAD_RET THREAD_FUNCTION_CALL PsySpaceContinuousRun(THREAD_ARG arg)
Thread entry for continuously running components.
~PsySpace()
Destructor.
Definition PsySpace.cpp:128
bool isConnected(uint32 timeoutMS=5000)
Wait until the space is fully connected to the system.
Definition PsySpace.cpp:226
MemoryManager * manager
The process-local memory manager: entry point to the node's shared-memory fabric. Owned by the space.
Definition PsySpace.h:339
friend class PsyAPI
Definition PsySpace.h:165
bool registerCrankCallback(const char *name, CrankFunction func)
Register a C/C++ entry function for a crank, to be invoked by the thread pool when the component trig...
Definition PsySpace.cpp:417
bool addPsyProbeCustomView(uint32 compID, const char *name, const char *templateURL)
Add a custom PsyProbe view tab for a component (space-level variant of PsyAPI::addPsyProbeCustomView(...
Definition PsySpace.cpp:336
PsyAPI * getCrankAPI(const char *name)
Get (or create) the PsyAPI handle for a crank hosted in this space.
Definition PsySpace.cpp:388
bool queryReply(uint32 id, uint8 status, DataMessage *result)
Reply to a previously received query.
Definition PsySpace.cpp:679
bool emitSignal(const PsyType &type, DataMessage *msg)
Emit a system-wide signal of the given type.
Definition PsySpace.cpp:503
bool logEntry(LogEntry *entry)
LogReceiver interface: accept a log entry produced within this space.
Definition PsySpace.cpp:346
PsySpace(const char *name=NULL, bool isAdHoc=true, uint16 procID=0, bool isLocal=false)
Create a space (does not yet join a system; call connect()).
Definition PsySpace.cpp:88
bool isRunning
Set by the worker while its loop is active.
virtual bool stop(uint32 timeout=200)
Ask the worker loop to finish and wait for it to do so.
uint32 threadID
ThreadManager slot ID of the worker thread (0 until known).
bool shouldContinue
Loop-continuation flag; cleared by stop().
static bool GetLocalThreadID(uint32 &id)
Look up the manager slot ID of the calling thread.
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
static bool TerminateThread(uint32 id)
Forcibly terminate the thread and release its slot.
Time-binned activation queue used by a PsySpace to drive time-triggered modules.
Definition PsySpace.h:91
DataMessage * waitForNextEvent(uint32 ms)
Block until the next scheduled event is due, or the timeout expires.
Definition PsySpace.cpp:61
bool removeSchedule(uint32 id)
Remove (and delete) a schedule by id.
Definition PsySpace.cpp:47
bool addSchedule(TimeQueueSchedule *schedule)
Add a schedule to the queue.
Definition PsySpace.cpp:36
~TimeQueue()
Destroy the queue and delete all remaining schedules (owned by the queue).
Definition PsySpace.cpp:27
TimeQueue()
Create an empty time queue with its own timer.
Definition PsySpace.cpp:23
One recurring (or time-bounded) activation schedule managed by a TimeQueue.
Definition PsySpace.h:61
uint32 msgTag
Tag attached to the generated message.
Definition PsySpace.h:80
uint64 start
First-activation time (µs), or 0 for immediate.
Definition PsySpace.h:76
PsyType msgType
Type of the message generated on each activation.
Definition PsySpace.h:79
uint32 interval
Repeat interval in milliseconds.
Definition PsySpace.h:75
TimeQueueSchedule(uint32 interval, uint64 start=0, uint64 end=0)
Create a schedule.
Definition PsySpace.cpp:13
~TimeQueueSchedule()
Destructor.
Definition PsySpace.cpp:19
uint64 end
Expiry time (µs), or 0 for no expiry.
Definition PsySpace.h:77
uint32 id
Unique schedule id, used for removal via TimeQueue::removeSchedule().
Definition PsySpace.h:72
bool waitNext()
Block until the next signal() occurs.
Definition Utils.cpp:1720
bool signal()
Wake all threads currently waiting on this event.
Definition Utils.cpp:1744
bool leave()
Release the mutex.
Definition Utils.cpp:1330
bool enter()
Block until the mutex is acquired.
Definition Utils.cpp:1158
Multiplexing timer: schedule many periodic timers and consume their expiries from one queue.
Definition Utils.h:686
std::string PrintTimeString(uint64 t, bool local=true, bool us=true, bool ms=true)
Definition PsyTime.cpp:676
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
std::string PrintTimeDifString(uint64 t, bool us=true, bool ms=true)
Definition PsyTime.cpp:722
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
Definition PsyTime.cpp:35
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
Definition PsyTime.cpp:25
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:3121
bool GetProcessCPUTicks(uint64 &ticks)
Get accumulated CPU time of the current process.
Definition Utils.cpp:3754
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:7497
std::string GetCommandLinePath()
Get the directory portion of the executable path.
Definition Utils.cpp:5678
double RandomValue()
Uniform random double in [0,1).
Definition Utils.cpp:9119
std::string TextTrimQuotes(const char *text)
Strip a single pair of surrounding quotes if present.
Definition Utils.cpp:7592
bool StringFormatInto(char *dst, uint32 maxsize, const char *format,...)
printf into a caller-supplied buffer with truncation.
Definition Utils.cpp:8037
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:8067
Library * OpenLibrary(const char *libName)
Load a library by name, applying platform filename conventions.
Definition Utils.cpp:5918
std::string TextCapitalise(const char *text)
Capitalise the first letter of text.
Definition Utils.cpp:7534
int8 Internal_QueryTest(PsyAPI *api)
Exercises query/queryReply round-trips.
int8 Internal_Pong(PsyAPI *api)
Latency test counterpart: answers pings.
int8(* CrankFunction)(PsyAPI *api)
Signature of a component (crank) entry function.
Definition PsySpace.h:49
int8 Internal_Print(PsyAPI *api)
Prints incoming messages to the console/log.
static struct PsyType CTRL_LOGPRINT
Definition ObjectIDs.h:84
int8 Internal_Simple(PsyAPI *api)
Pass-through test crank: copies each incoming message to its posts.
int8 Internal_SignalPong(PsyAPI *api)
Signal-based pong counterpart.
int8 Internal_StatsLog(PsyAPI *api)
Periodically logs system statistics.
int8 Internal_SignalPing(PsyAPI *api)
Signal-based ping test.
THREAD_RET THREAD_FUNCTION_CALL PsySpaceContinuousRun(THREAD_ARG arg)
Continuous-component thread entry: runs PsySpace::runContinuousComponent() for the space passed in ar...
int8 Internal_MessageToggler(PsyAPI *api)
Alternates/toggles message output for testing.
int8 Internal_Shutdown(PsyAPI *api)
Initiates system shutdown when triggered.
THREAD_RET THREAD_FUNCTION_CALL PsySpaceRun(THREAD_ARG arg)
Main service-thread entry: runs PsySpace::run() for the space passed in arg.
int8 Internal_Ping(PsyAPI *api)
Latency test: posts pings, expects pongs.
int8 Internal_Time(PsyAPI *api)
Posts time information/timestamps.
THREAD_RET THREAD_FUNCTION_CALL PsySpacePoolRun(THREAD_ARG arg)
Pool worker-thread entry: runs PsySpace::threadPoolDispatch() for the space passed in arg.
int8 Internal_BitmapPoster(PsyAPI *api)
Posts test bitmap/image messages.
int8 Internal_MessageTypeConverter(PsyAPI *api)
Converts incoming messages to another type before reposting.
int8 Internal_MessageScript(PsyAPI *api)
Plays back scripted TrackPlayerMessage sequences.
int8 Internal_RetrieveTest(PsyAPI *api)
Exercises whiteboard retrieve calls.
Hierarchical message type identifier — the key used for publish/subscribe matching in Psyclone.
Definition Types.h:123
uint64 memid
Shared-memory id assigned when the message is stored in a memory map.
Wire/storage layout of one log record: fixed header immediately followed by the message text.
Definition Utils.h:228
uint32 size
Definition Utils.h:229
Per-signal bookkeeping for PsySpace signal distribution.
Definition PsySpace.h:130
utils::Mutex mutex
Protects lastSignalMsg.
Definition PsySpace.h:133
DataMessage * lastSignalMsg
Most recently emitted message for this signal type (owned by the space).
Definition PsySpace.h:131
utils::Event event
Signalled whenever a new message arrives; wakes waiters.
Definition PsySpace.h:132
A complete trigger definition: what fires a component's crank and what happens then.
uint16 crankID
Id of the crank (processing function) to run when fired.