C API reference
A compact C ABI for shared-memory messaging, with opaque handles, explicit status codes, and caller-controlled ownership.
Public C header ↗ · Prebuilt SDK downloads ↗
Install the SDK
Download the archive for your platform. This example uses the GitHub CLI on macOS Apple Silicon:
gh release download native-v3.0.1 --repo cloudtoid/interprocess --pattern "*-darwin-arm64.tar.gz"
mkdir -p cloudtoid-sdk
tar -xzf cloudtoid-interprocess-3.0.1-darwin-arm64.tar.gz -C cloudtoid-sdk --strip-components=1
export PKG_CONFIG_PATH="$PWD/cloudtoid-sdk/lib/pkgconfig:$PKG_CONFIG_PATH"
Other archive suffixes are darwin-x64, linux-arm64, linux-x64, and win32-x64. Replace the suffix in both archive names. Linux prebuilt libraries require glibc 2.34 or later.
On Windows, extract the archive, set PKG_CONFIG_PATH to its lib/pkgconfig directory, and add its lib directory to PATH for the DLL. Link against the import library. The header can also be included from C++.
On Unix, compile a C program with pkg-config; the SDK flags include the runtime library search path:
cc example.c -o example $(pkg-config --cflags --libs cloudtoid-interprocess)
./example
Send and receive
#include <interprocess.h>
#include <stdio.h>
int main(void) {
cip_subscriber *subscriber = NULL;
cip_publisher *publisher = NULL;
int result = 1;
if (cip_subscriber_open("example", NULL, 65536, &subscriber) != CIP_OK)
goto cleanup;
if (cip_publisher_open("example", NULL, 65536, &publisher) != CIP_OK)
goto cleanup;
if (cip_try_send(publisher, (const uint8_t *)"hello", 5) != CIP_OK)
goto cleanup;
cip_buffer message;
if (cip_receive(subscriber, 1000, &message) == CIP_OK) {
fwrite(message.data, 1, message.length, stdout);
cip_buffer_free(message);
result = 0;
}
cleanup:
cip_publisher_close(publisher);
cip_subscriber_close(subscriber);
return result;
}
Types and status codes
cip_publisher and cip_subscriber are opaque handles. The returned message type is:
typedef struct cip_buffer {
uint8_t *data;
size_t length;
} cip_buffer;
Functions returning int32_t status use CIP_OK = 1 for success, CIP_UNAVAILABLE = 0 for full/empty/timeout or temporary recovery, and CIP_ERROR = -1 for failure. An empty message is still CIP_OK with length zero.
Open and close
int32_t cip_publisher_open(const char *name, const char *path, size_t capacity, cip_publisher **output)
int32_t cip_subscriber_open(const char *name, const char *path, size_t capacity, cip_subscriber **output)
Create or join a queue and write the handle to output on success. Strings are UTF-8 and NUL-terminated. Path may be NULL for the temporary directory; Windows ignores it. Capacity must match all participants.
void cip_publisher_close(cip_publisher *handle)
void cip_subscriber_close(cip_subscriber *handle)
Release a handle exactly once, after every concurrent call has returned. Close(NULL) is safe. A non-null handle cannot be reused or closed again after release.
Send and receive
int32_t cip_try_send(const cip_publisher *handle, const uint8_t *data, size_t length)
Copies one message. CIP_UNAVAILABLE means no room or recovery admission unavailable. Input memory must remain valid for the call; a NULL data pointer is allowed only with length zero.
int32_t cip_receive(const cip_subscriber *handle, int64_t timeout_ms, cip_buffer *output)
Copies and consumes a message into an owned buffer. Timeout is milliseconds: -1 waits indefinitely, 0 attempts once, and a positive value bounds the wait. Other negative values are invalid. CIP_UNAVAILABLE means no ready message before the timeout.
void cip_buffer_free(cip_buffer buffer)
Free each successful receive result exactly once, including empty messages. Do not free a result from an unsuccessful receive or invent a buffer to pass here. Copying the struct does not create a second ownership right.
int32_t cip_try_receive_into(const cip_subscriber *handle, uint8_t *data, size_t capacity, size_t *copied)
Tries once and copies into caller-owned storage. On success, copied is the number of bytes written. An undersized buffer truncates and consumes the message. NULL data requires capacity zero. Use the status to distinguish an empty message from no message.
Error details
const char *cip_last_error(void)
Diagnostic text in thread-local storage, valid until the next error on that thread. Read or copy it immediately after CIP_ERROR, on the same OS thread.
int32_t cip_last_error_kind(void)
Returns a cip_error_kind: CIP_NO_ERROR = 0, CIP_INVALID_ARGUMENT = 1, CIP_CAPACITY_MISMATCH = 2, CIP_PUBLISHER_LIMIT = 3, CIP_EXHAUSTED = 4, CIP_CORRUPT = 5, CIP_IO_ERROR = 6, or CIP_INTERNAL_ERROR = 7. Error state is diagnostic state, not a substitute for checking each function's return status.
Concurrency and shutdown
Handles support concurrent operations, but closing concurrently with an operation is unsafe. Use bounded receive timeouts, signal your worker to stop, join it, then close the handle. There is no cancellation object or batch-send function in the C ABI. Open after fork; inherited handles must not be used in the child.
Prebuilt SDKs contain shared libraries. For static linking, build cloudtoid-interprocess-ffi from source and define CIP_STATIC on Windows. See the source-build instructions.
Build from source
From the repository root, with Rust and CMake installed:
cmake -S src/c -B target/c-sdk -DCMAKE_INSTALL_PREFIX="$HOME/.local"
cmake --build target/c-sdk --config Release
cmake --install target/c-sdk --config Release
Link with pkg-config --cflags --libs cloudtoid-interprocess. On Unix, these flags embed the installed library directory as a runtime search path. On Windows, add the DLL directory to PATH or place the DLL beside the executable.
For a static library, run cargo build --release -p cloudtoid-interprocess-ffi and define CIP_STATIC when compiling Windows callers.