CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
Going Deeper

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.

Shared memory: the backbone — and its ABI rule

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.

Warning
The ABI rule. The structs in these headers are mapped simultaneously into multiple processes: their size, field order and alignment are a de-facto ABI. If you change any shared struct — or update the library — you must rebuild every binary that maps the segments (make clean && make). Mixing binaries built from different layouts silently corrupts shared memory across processes; there is no compiler diagnostic. This is also why make clean matters when switching debug/release.

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.

Threading model

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.

The request fabric

For distributed request/response workloads (as opposed to pub/sub), CMSDK ships a three-role fabric (Requests, Networking):

  • cmlabs::RequestClient — wraps each outgoing cmlabs::DataMessage in a RequestReply "future"; block on waitForResult() or register a callback.
  • cmlabs::RequestGateway — the router/load balancer. Accepts requests from many clients over binary, HTTP(S) or WebSocket on the same ports via protocol auto-detection, queues them, picks an executor and routes replies back. Clients and executors dial in; a gateway does not dial out to clients or executors (though gateways can link to other gateways via addGateway), which simplifies firewalling. Includes a call log and an embedded web server.
  • cmlabs::RequestExecutor — the worker. Connects out to gateways, announces itself with heartbeats (used for liveness and load balancing), and separates short (interactive) from long (expensive) request queues so long jobs cannot starve short ones.

This fabric is independent of Psyclone systems — you can use it as a stand-alone RPC/work-distribution layer in any C++ program.

The built-in HTTP server (and PsyProbe)

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.

UDP datagrams

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).

The packed wire form

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().

Kotlin and Android

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").

Microsecond time

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.

SSL

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.

Utilities and the rest

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).

Reference groups

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