|
CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
|
Everything above is enough to build systems. This page is the expert track: the machinery underneath, and the parts of CMSDK that are valuable even outside Psyclone. Each section links into the reference groups generated from the source, where the full details live.
A Psyclone node's inter-process state lives in named shared-memory segments owned by the cmlabs::MemoryManager singleton (Shared-Memory Subsystem): a master segment plus segments for the temporal message store (cmlabs::TemporalMemory), the process/queue tables (cmlabs::ProcessMemory), the component registry with parameters and stats (cmlabs::ComponentMemory) and the name↔id maps (cmlabs::DataMapsMemory). On Windows these are named file mappings; on POSIX they are shm_open/mmap regions. Processes attach by name, which is what makes crash isolation and re-attach possible.
Messages themselves are cmlabs::DataMessage objects (Messaging): one flat allocation, copied verbatim into shared memory or onto a socket, with typed user entries, arrays and maps, and no serialisation step anywhere.
cmlabs::ThreadManager (Threading) is the process-wide registry of every native thread the SDK spawns, with per-thread CPU statistics. The scheduling policy above it maps crank work onto threads two ways: a continuous crank owns a dedicated thread for its lifetime, while a one-shot crank borrows a pool thread each time its trigger fires — which is why the same crank function can run concurrently on several threads and must guard shared state (see Writing a Module (Crank)). Synchronisation primitives (utils::Mutex, events, wait queues) and portable thread wrappers live in Utility functions and objects. Prefer cooperative shutdown (loop on cmlabs::PsyAPI::shouldContinue(), Runnable::stop()) — asynchronously killed threads do not unwind.
For distributed request/response workloads (as opposed to pub/sub), CMSDK ships a three-role fabric (Requests, Networking):
This fabric is independent of Psyclone systems — you can use it as a stand-alone RPC/work-distribution layer in any C++ program.
cmlabs::NetworkManager (Networking) turns any port into a full HTTP(S)/WebSocket server via createListener() with PROTOCOL_HTTP_SERVER — this is exactly how the PsyProbe monitoring UI is served. It also provides an HTTP(S) client (makeHTTPRequest(), including multipart POST), protocol auto-detection per listener, and typed dispatch of parsed protocol objects (HTTPRequest, DataMessage, WebsocketData) either to callbacks or to blocking waitFor*() calls. HTML generation helpers live in HTML.h, REST helpers in RESTAPI.h.
WebSocket rides on the HTTP server protocol: the server loop answers the upgrade itself and then delivers frames to receiveWebsocketData(), so one port serves HTTP and WebSocket, decided per connection. The client side, createWebsocketConnection(), takes http:// / https:// URLs.
createUDPConnection() with PROTOCOL_MESSAGE binds a UDP port and delivers each datagram's DataMessage to receiveMessage(). The sender of the message being delivered is channel->getRemoteAddress(conid) (each datagram is read on its own, so the address always matches the message), and channel->sendMessage(msg, conid, receiver) answers it from the bound port — the only way a reply gets back through NAT to a phone or a home router. Listening sockets ask for a 4 MB receive buffer so a client draining a backlog is not dropped by the kernel. UDP itself gives no ordering or retransmission; one DataMessage must fit in one datagram (< 64 KB).
A regular DataMessage goes on the wire as its memory block: a 200-byte header and 16 bytes of headers per entry, mostly zeros on a message from a device. DataMessage::toPacked() writes the same content compactly (non-zero header fields and entries as varints; layout in DataMessage.h) and DataMessage::FromPacked() turns it back into a regular message, byte for byte. A phone audio frame with 80 B of Opus is 637 B regular and 253 B packed. Packed frames carry their own object id (PACKEDDATAMESSAGEID) after the size, so MessageProtocol reads both forms on the same TCP or UDP port; send one with channel->sendMessage(msg, conid, receiver, true). Psyclone services answer each peer in the form it used. The Kotlin codec has toPackedBytes() and reads both in fromBytes().
CMSDK/kotlin is a byte-exact Kotlin implementation of the DataMessage wire format (package cmlabs.cmsdk, pure JVM), so an Android app or a JVM tool can build and read messages for Psyclone services without linking the C++ library. Its tests are generated from the C++ library itself (header layout via offsetof(), golden messages written by C++), and make kotlin reruns them; see kotlin/README.md.
Psyclone builds its custom services on exactly these pieces: a <service> reached through HTTP, WebSocket and UDP interfaces, with edge clients naming posts instead of setting message types (see the Psyclone User Guide, "Services & Interfaces").
All timestamps in the system are 64-bit integer microsecond times (Functions dealing with time): GetTimeNow(), GetTimeAge(), conversion and pretty-printing helpers, plus cross-node time synchronisation state in the master shared segment. Message creation/posting times are captured at microsecond resolution — the ping-pong example measures per-message latency this way.
Build with the SSL targets (see Compiling and linking with the CMSDK library) to enable TLS in the networking stack — server and client side — via the OpenSSL-backed wrappers in PsySSL.h. The SSL library variants carry SSL in their file names.
Utility functions and objects is the everything-drawer that makes the rest portable: strings and formatting (utils::StringFormat), files and directories, processes and dynamic libraries, sockets, shared-memory primitives, timers, compression, Base64 (Base64.h), bitmaps (Bitmap.h), maths and statistics (MathClasses.h, Stats.h, MovingAverage.h, Observations.h), and a unit-test framework (Unit Test Framework, UnitTestFramework.h) used by the SDK's own test suite (Examples/psytest.xml, Examples/src/PsyTest.cpp).
| Group | Contents |
|---|---|
| Messaging | DataMessage, binary containers, message protocol |
| Shared-Memory Subsystem | Shared-memory segments, maps, queues, allocators |
| Threading | ThreadManager, thread stats and lifecycle |
| Networking | NetworkManager, HTTP/WebSocket server and client |
| Requests | RequestClient / RequestGateway / RequestExecutor |
| Functions dealing with time | Microsecond time functions |
| Unit Test Framework | Unit-test framework |
| Utility functions and objects | Utility functions and objects |