C
The whole API is one header: 30 functions, four handles and eleven result codes, with the C calling convention. Every binding is a thin layer over these calls.
Build and link
the Zig build system · no CMakeThe recommended way to build C (and C++) against FastIPC is the Zig build system: no CMake or other build tool, and Zig 0.17.0 is a single download (a program written in Zig imports the Zig module instead). It builds the library for your target and compiles your C with its bundled Clang. After zig fetch --save git+https://github.com/fastipc/fastipc, your build.zig links the dependency's artifact fastipc, the shared library, which also puts fipc.h and fipc.hpp on the include path. The repository's examples/c-cpp is such a project, with the servers and clients of this page and the C++ page.
Any other compiler works too. Take the headers and the library from the GitHub release: an archive per OS with include/, and lib/libfastipc.so (Linux x64 or arm64), lib/libfastipc.dylib (macOS 14.4+ on Apple Silicon) or bin/fastipc.dll with its import library lib/fastipc.lib (Windows x64). Or build them in a checkout with zig build -Doptimize=ReleaseFast: zig-out/include, zig-out/lib and, on Windows, zig-out/bin. On Linux, the program finds libfastipc.so through an rpath, LD_LIBRARY_PATH or a system library directory. On macOS, the dylib's install name is @rpath/libfastipc.dylib, so link the program with an rpath to its folder; the dylib is ad hoc signed, not notarized, so after a browser download clear the archive's quarantine flag with xattr -d com.apple.quarantine lib/libfastipc.dylib (an app with the hardened runtime and library validation signs the dylib it embeds with its own identity). On Windows, define FASTIPC_SHARED so the header declares the functions dllimport, and put fastipc.dll next to the program or on PATH. Every build targets x86-64-v3 (on macOS, Apple M1 and macOS 14.4).
const fastipc = b.dependency("fipc", .{
.target = target,
.optimize = optimize,
}).artifact("fastipc");
exe.root_module.addCSourceFile(.{
.file = b.path("src/server.c"),
.flags = &.{"-std=c11"},
});
exe.root_module.addCMacro("FASTIPC_SHARED", "1"); // Windows
exe.root_module.linkLibrary(fastipc);
b.installArtifact(fastipc); // the library next to the programs# Linux, from the release archive
cc server.c -Iinclude -Llib -lfastipc -o server
LD_LIBRARY_PATH=lib ./server # then ./client in another terminal
# macOS, from the release archive
xattr -d com.apple.quarantine lib/libfastipc.dylib # if a browser downloaded it
cc server.c -Iinclude -Llib -lfastipc -Wl,-rpath,@executable_path/lib -o server
./server
# Windows, MSVC (a developer prompt)
cl /DFASTIPC_SHARED /Iinclude server.c lib\fastipc.lib
copy bin\fastipc.dll .Server and client
two processes, one nameRun the server in one process, then the client in another; fipc_stream_connect waits up to its timeout for a server to create the stream and accept. The pair makes the exchange every example on this site makes, the message ping, answered in upper case: this client works with any language's server, and this server with any language's client (every pair of languages is tested).
#include <ctype.h>
#include <fipc.h>
int main(void)
{
fipc_listener_t* listener;
if (fipc_stream_create("my_channel", 1 << 20, &listener) != FIPC_OK) /* rings of 1 MiB each way */
return 1;
fipc_conn_t* conn;
if (fipc_stream_accept(listener, &conn, FIPC_FOREVER) == FIPC_OK) /* waits for a client */
{
char buf[256];
size_t len;
if (fipc_recv(conn, buf, sizeof buf, &len, FIPC_FOREVER) == FIPC_OK)
{
for (size_t i = 0; i < len; i++)
buf[i] = (char) toupper((unsigned char) buf[i]);
fipc_send(conn, buf, len, FIPC_FOREVER);
}
fipc_close(conn);
}
fipc_listener_close(listener);
return 0;
}#include <fipc.h>
#include <stdio.h>
int main(void)
{
fipc_conn_t* conn;
fipc_result_t result = fipc_stream_connect("my_channel", &conn, 5000);
if (result != FIPC_OK)
{
fprintf(stderr, "connect: %s\n", fipc_result_str(result));
return 1;
}
char buf[256];
size_t len;
if (fipc_send(conn, "ping", 4, FIPC_FOREVER) == FIPC_OK
&& fipc_recv(conn, buf, sizeof buf, &len, FIPC_FOREVER) == FIPC_OK)
printf("%.*s\n", (int) len, buf); /* PING */
fipc_close(conn);
return 0;
}The rules
the header's conventionsTimeouts
Always the last parameter, in milliseconds. FIPC_NO_WAIT (0) doesn't wait; FIPC_FOREVER (−1) waits for ever. A call that runs out returns FIPC_TIMEOUT having changed nothing.
Names and rings
A name is 1 to 245 characters of [A-Za-z0-9_.-], not starting with . or -, local to the user (Linux, macOS) or the desktop session (Windows). The name's creator picks the ring size: for a stream, a power of two from 1 KiB to 2 GiB.
One sender, one receiver
On a connection, one thread at a time sends and one receives, and the two may run at once. A broadcast's, a queue's or a service's owner needs no thread for its joiners: its process admits them on a thread of the library's, so it may join from any thread. Cancels work from any thread. To stop a waiting thread: cancel, join it, then close.
Messages
A message holds at least one byte and arrives in order (a work queue promises no order; a service's replies arrive in the order of their requests). The copying calls take any size; a long message travels in pieces and is delivered whole or not at all.
Buffers too small
A receive into a buffer that is too small takes nothing and reports the length, so allocate that much and call again. Pass a zero-length buffer to ask for the length only.
The peer's end
Final for that connection: sends return it at once, receives after every completed message. Close the connection, then accept or connect a new one.
Zero-copy
messages of one piece: up to fipc_max_piece(conn) bytesReserve room in the ring, write the message there, publish it. On the other side, read it where it lies and release it. Pointers are 16-byte aligned. fipc_max_piece is the ring's capacity less 64 bytes; anything longer goes through fipc_send.
void* out;
if (fipc_send_acquire(conn, 64, &out,
FIPC_FOREVER) == FIPC_OK) {
int n = snprintf(out, 64, "tick %d", tick);
fipc_send_commit(conn, (size_t) n); /* publish n bytes */
}const void* data;
size_t len;
if (fipc_recv_acquire(conn, &data, &len,
FIPC_FOREVER) == FIPC_OK) {
handle(data, len); /* read-only, in the ring */
fipc_recv_release(conn); /* frees its room */
}Broadcast
one publisher, many subscribersA broadcast sends every message of one publisher to each of its subscribers, in any processes of the user; each message is stored once, in memory every subscriber maps. Messages are of one piece, up to a quarter of the capacity. fipc_broadcast_create returns the publisher's connection; the publisher's process admits the subscribers on a thread of the library's, so no thread of yours waits for them. The publisher waits for the first one (fipc_broadcast_wait_for_members) before it sends tick 1 to tick 3; its close ends the broadcast and frees the name. The subscriber joins gated (fipc_broadcast_join with FIPC_SUBSCRIBER_GATED), so it misses nothing (the publisher waits for it), and receives until FIPC_DISCONNECTED, which comes after the last message. A lossy subscriber (FIPC_SUBSCRIBER_LOSSY) never holds the publisher back: one that falls a lap behind gets FIPC_OVERRUN, with the number of messages lost in *out_len. A gated one that holds the publisher back for the broadcast's max_stall_ms is unsubscribed (FIPC_UNSUBSCRIBED).
#include <fipc.h>
#include <stdio.h>
int main(void)
{
fipc_conn_t* publisher;
/* 64 KiB of messages, up to 8 subscribers; one that holds the publisher back 5 s is dropped */
if (fipc_broadcast_create("my_broadcast", 1 << 16, 8, 5000, &publisher) != FIPC_OK)
return 1;
uint32_t count;
/* waits for the first subscriber */
if (fipc_broadcast_wait_for_members(publisher, 1, &count, FIPC_FOREVER) == FIPC_OK)
{
for (int i = 1; i <= 3; i++)
{
char tick[16];
int len = snprintf(tick, sizeof tick, "tick %d", i);
fipc_send(publisher, tick, (size_t) len, FIPC_FOREVER);
}
}
fipc_close(publisher); /* ends the broadcast: subscribers get FIPC_DISCONNECTED after the ticks */
return 0;
}#include <fipc.h>
#include <stdio.h>
int main(void)
{
fipc_conn_t* subscriber;
/* gated: misses nothing (the publisher waits for it) */
fipc_result_t result = fipc_broadcast_join("my_broadcast", FIPC_SUBSCRIBER_GATED, &subscriber, 5000);
if (result != FIPC_OK)
{
fprintf(stderr, "join: %s\n", fipc_result_str(result));
return 1;
}
char buf[256];
size_t len;
while ((result = fipc_recv(subscriber, buf, sizeof buf, &len, FIPC_FOREVER)) == FIPC_OK)
printf("%.*s\n", (int) len, buf); /* tick 1, tick 2, tick 3 */
fipc_close(subscriber);
return result == FIPC_DISCONNECTED ? 0 : 1; /* the publisher closed */
}Work queue
many producers, each message to one consumerA work queue takes messages from up to 63 producers and hands each to one of up to 64 consumers, in any processes of the user. Each producer has a ring of its own; a consumer takes a message of any producer, and the queue promises no order. Messages are of one piece, up to a quarter of the capacity, and wait in the queue until a consumer takes them. Each message goes to one consumer, and again, to another, only if that consumer's process ends before acknowledging it (delivery, below). Here the producer creates the queue (fipc_queue_create: a fipc_queue_t) and joins it as its producer with fipc_queue_join, as any process does: the owner's process admits every joiner on a thread of the library's, so the thread that created the queue may join it. It sends job 1 to job 3, waits for the consumer (fipc_queue_wait_for_members) and closes the queue (fipc_queue_close), which ends it: the consumer receives what is left, then FIPC_DISCONNECTED. Producers and consumers of other processes join with fipc_queue_join (FIPC_PRODUCER, FIPC_CONSUMER); a producer's or a consumer's close never ends the queue.
#include <fipc.h>
#include <stdio.h>
int main(void)
{
fipc_queue_t* queue;
fipc_conn_t* producer = NULL;
/* 4 KiB of messages per producer, 1 producer and 1 consumer: this process owns the queue and is its producer */
if (fipc_queue_create("my_queue", 4096, 1, 1, &queue) != FIPC_OK)
return 1;
fipc_result_t result = fipc_queue_join("my_queue", FIPC_PRODUCER, &producer, 5000);
for (int i = 1; i <= 3 && result == FIPC_OK; i++)
{
char job[16];
int len = snprintf(job, sizeof job, "job %d", i);
result = fipc_send(producer, job, (size_t) len, FIPC_FOREVER); /* waits in the queue for a consumer */
}
uint32_t count;
/* waits for the consumer to join */
if (result == FIPC_OK)
result = fipc_queue_wait_for_members(queue, FIPC_CONSUMER, 1, &count, FIPC_FOREVER);
fipc_close(producer);
fipc_queue_close(queue); /* ends the queue: the consumer gets FIPC_DISCONNECTED after the jobs */
return result == FIPC_OK ? 0 : 1;
}#include <fipc.h>
#include <stdio.h>
int main(void)
{
fipc_conn_t* consumer;
fipc_result_t result = fipc_queue_join("my_queue", FIPC_CONSUMER, &consumer, 5000);
if (result != FIPC_OK)
{
fprintf(stderr, "join: %s\n", fipc_result_str(result));
return 1;
}
char buf[256];
size_t len;
while ((result = fipc_recv(consumer, buf, sizeof buf, &len, FIPC_FOREVER)) == FIPC_OK)
printf("%.*s\n", (int) len, buf); /* job 1, job 2, job 3 */
fipc_close(consumer);
return result == FIPC_DISCONNECTED ? 0 : 1; /* the queue ended */
}Delivery. Each message goes to one consumer. It goes again, to another consumer, only if the process of the consumer that took it ends before acknowledging it, and when a consumer acknowledges is its own choice, not the queue's. A message received in place is acknowledged at its release (or the consumer's next receive, or its close).
| How the consumer receives | When it acknowledges | If its process ends | What the application gets |
|---|---|---|---|
fipc_recv (copying) | inside the call, before it returns | inside the call: the message goes to another consumer, and this one never saw it; after the call returned: it doesn't go again | at most once: no duplicates |
fipc_recv_acquire, then fipc_recv_release at once, then the work | at the release, before the work | as above | at most once: no duplicates |
fipc_recv_acquire, the work, then fipc_recv_release | at the release, after the work | before the release: the message goes to another consumer | at least once: a duplicate is possible, so the work must be idempotent |
- Choose by the work, per consumer. Work that may safely run twice (rendering, a computation, re-indexing, converting a file) acknowledges after it; work whose effects must never repeat (a payment, an email, a motor command) acknowledges before it.
- Not covered: a consumer that hangs while its process runs (there are no timeouts, by design); the owner's process, with which the queue ends; a reboot. A consumer holds at most one unacknowledged message at a time, the one received in place.
- A message that goes again comes in no set order, after messages its producer sent later: the queue promises no order anyway.
- A message held in place holds back its producer's room until its release: a consumer that acknowledges after a long job keeps that message's room, and so the room of the producer's messages after it, until then.
- A message that crashes every consumer that takes it, acknowledged after the work, goes to each consumer in turn, with no limit: acknowledge before the work where that can happen.
Service
many clients, a pool of servers, with repliesA service takes requests from up to 64 clients and hands each to one of up to 64 servers, in any processes of the user; the server that took a request replies to it, and a client receives its replies in the order of its requests. Here the server creates the service (fipc_service_create: a fipc_service_t) and joins it as its server with fipc_service_join (FIPC_SERVER), answers three requests with their uppercase and closes the service (fipc_service_close), which ends it; the client joins (FIPC_CLIENT), sends ping 1 to ping 3 and prints each reply. A request whose server ended before it replied gets FIPC_ABANDONED.
#include <ctype.h>
#include <fipc.h>
int main(void)
{
fipc_service_t* service;
fipc_conn_t* server = NULL;
/* 4 KiB per client, requests and replies of up to 256 bytes, 1 client and 1 server: this process owns the service
and is its server */
if (fipc_service_create("my_service", 4096, 256, 1, 1, &service) != FIPC_OK)
return 1;
fipc_result_t result = fipc_service_join("my_service", FIPC_SERVER, &server, 5000);
for (int i = 1; i <= 3 && result == FIPC_OK; i++) /* three requests, then the end */
{
char buf[256];
size_t len;
result = fipc_recv(server, buf, sizeof buf, &len, FIPC_FOREVER); /* the next request of any client */
for (size_t j = 0; result == FIPC_OK && j < len; j++)
buf[j] = (char) toupper((unsigned char) buf[j]);
if (result == FIPC_OK)
result = fipc_send(server, buf, len, FIPC_NO_WAIT); /* the reply goes to its client, and never waits */
}
fipc_close(server);
fipc_service_close(service); /* ends the service: the client still receives the replies sent */
return result == FIPC_OK ? 0 : 1;
}#include <fipc.h>
#include <stdio.h>
int main(void)
{
fipc_conn_t* client;
fipc_result_t result = fipc_service_join("my_service", FIPC_CLIENT, &client, 5000);
if (result != FIPC_OK)
{
fprintf(stderr, "join: %s\n", fipc_result_str(result));
return 1;
}
for (int i = 1; i <= 3 && result == FIPC_OK; i++)
{
char buf[256];
size_t len = (size_t) snprintf(buf, sizeof buf, "ping %d", i);
result = fipc_send(client, buf, len, FIPC_FOREVER); /* a request: a server takes it */
if (result == FIPC_OK)
result = fipc_recv(client, buf, sizeof buf, &len, FIPC_FOREVER); /* its reply */
if (result == FIPC_OK)
printf("%.*s\n", (int) len, buf); /* PING 1, PING 2, PING 3 */
}
if (result != FIPC_OK) /* FIPC_ABANDONED: the server ended before it replied */
fprintf(stderr, "request: %s\n", fipc_result_str(result));
fipc_close(client);
return result == FIPC_OK ? 0 : 1;
}The calls. A service's connections use the calls every connection has; what each does depends on the role. Requests and replies are of one piece, 1 to max_message bytes, the owner's choice at fipc_service_create (1 to a quarter of the capacity), and fipc_max_piece returns it on both roles, so a buffer of that size takes every request and every reply.
| Call | On a client | On a server |
|---|---|---|
fipc_send, fipc_send_acquire / _commit | Sends a request. Waits up to its timeout for room in the client's ring. | Replies to the request it holds; never waits. FIPC_INVALID with no request held, FIPC_TOO_LARGE over fipc_max_piece. |
fipc_recv | The reply to its oldest request whose reply it hasn't received, waiting for it if it hasn't come; FIPC_ABANDONED (with *out_len 0) if that request's server ended before it replied. | The next request of any client, copied; the server then holds it until it replies. |
fipc_recv_acquire / fipc_recv_release | The next reply in place, until the release, the next receive or the close. | The next request in place, valid until the reply's fipc_send or fipc_send_commit; the release does nothing. |
fipc_close | Leaves the service: requests no server took are dropped, and the replies to those already taken are lost. | Leaves the service; a request it still holds is answered FIPC_ABANDONED. |
- Replies come back in the order of the requests, so the n-th reply a client receives answers its n-th request, and a request carries no id. A client may send several requests before it receives, as many as its ring has room for, and the servers work on them at once. A receive that times out or is cancelled leaves its reply to come: the next receive returns it. Servers take each client's requests in the order it sent them, and the requests of different clients in no set order.
- A server never waits for a client. A request takes room in its client's ring for itself and for its reply: its length rounded up to 64 bytes, plus
max_messagerounded up to 64, from its send until its reply has been received. A client that keeps sending without receiving waits for room its own replies hold (with one thread, it times out). - A server holds one request at a time: until it replies, its receives return
FIPC_INVALID. It may receive on one thread and reply on another. A pool is several servers, each a connection of its own, on threads or in processes; each takes the next request of any client. A reply to a client that has left is dropped and returnsFIPC_OK. - A request is answered once: by its reply, or with
FIPC_ABANDONEDif the server that took it ended before it replied (its process ended, or it closed its connection). An abandoned request may or may not have been carried out; the service never sends it again, and the client decides whether to. Not covered: a server that hangs while its process runs (there are no timeouts: its client waits within its own timeouts), and the owner's process, with which the service ends. - The owner's handle.
fipc_service_wait_for_memberscounts the joined clients (FIPC_CLIENT), servers (FIPC_SERVER) or both (0), and waits for at least a given number;fipc_service_cancelends those waits;fipc_service_closeends the service. The owner joins its own service, as either role, from any thread. A member's close, or the end of its process, never ends the service: clients wait while no server is joined, and servers while no client is. - The end (
fipc_service_close, or the owner's process ending): clients' sends and servers' receives returnFIPC_DISCONNECTED; a client receives the replies that came before the end, in order, thenFIPC_DISCONNECTED; a server's reply returnsFIPC_DISCONNECTED. A reply that returnedFIPC_OKis always received by a client that receives.
All 30 functions
the exports, exactly| Function | What it does |
|---|---|
fipc_stream_create | Server: claims a name for a stream and listens on it with rings of a given capacity, and returns its listener. Doesn't wait. |
fipc_stream_accept | Server: waits for a client, sets its connection up on the calling thread and returns it. One client at a time. A call that times out keeps a client's setup for the next, so timeout 0 polls. |
fipc_listener_cancel | Makes every waiting and later accept return FIPC_CANCELLED, dropping a client whose setup began. Any thread. |
fipc_listener_close | Stops listening and frees the listener; accepted connections stay open. |
fipc_stream_connect | Client: connects to the stream on a name and sets the connection up on the calling thread, waiting for a server to create it and accept: client and server run on different threads or processes. |
fipc_max_piece | The longest message the zero-copy calls take: the capacity less 64 bytes (a broadcast's or a queue's connections: a quarter of the capacity, for every message; a service's: its max_message). |
fipc_cancel | Makes every waiting and later call on the connection return FIPC_CANCELLED. Any thread. |
fipc_close | Ends the connection (the peer gets FIPC_DISCONNECTED), unmaps its rings, frees the handle. |
fipc_send | Sends one message of any size, copying it in, in pieces if needed. |
fipc_recv | Receives one message of any size into your buffer. |
fipc_send_many | Sends several messages in one call, in order, and publishes them together, with at most one wake of the receivers; before any wait for room it publishes what it wrote. Every message is checked before any goes, and *out_sent says how many went. Batch both ends: a receiver that takes one message per call falls behind a batching sender. |
fipc_recv_many | Waits for the next message, then takes every further message already there that fits your buffer, packed back to back, each one's length in an array of lengths (a broadcast's subscriber: those its publisher has moved past). Any result but FIPC_OK is the first message's, as fipc_recv returns it. |
fipc_send_acquire | Zero-copy send, step 1: reserves contiguous room in the ring. |
fipc_send_commit | Zero-copy send, step 2: publishes the reserved bytes as one message. |
fipc_recv_acquire | Zero-copy receive, step 1: the next message, in place and read-only. |
fipc_recv_release | Zero-copy receive, step 2: frees the message's room. |
fipc_broadcast_create | Publisher: claims a name for a broadcast of a given capacity and number of subscribers, and returns the publisher's connection, whose sends go to every subscriber; its process admits the subscribers on a thread of the library's. Its close ends the broadcast and frees the name. Doesn't wait. |
fipc_broadcast_join | Subscriber: joins the broadcast on a name, lossy (FIPC_SUBSCRIBER_LOSSY) or gated (FIPC_SUBSCRIBER_GATED), and returns its connection, waiting for the publisher to create it, to have a free slot and to admit it. It starts at the publisher's next message as of its admission: a gated joiner holds the publisher back (max_stall_ms counts) from then on, and a lossy one on a busy broadcast may get FIPC_OVERRUN on its first receive. |
fipc_broadcast_wait_for_members | Publisher: how many subscribers are joined; waits until at least a given number are, so that nothing is sent before they are there. Any thread. |
fipc_queue_create | Owner: claims a name for a work queue of a given ring capacity per producer and numbers of producers and consumers, and returns the queue's handle (fipc_queue_t); its process admits the members on a thread of the library's. Doesn't wait. |
fipc_queue_join | Producer or consumer (FIPC_PRODUCER, FIPC_CONSUMER): joins the work queue on a name and returns its connection, waiting for the owner to create it, for a free place and to admit it. The owner joins its own queue this way too, from any thread. Each message goes to one consumer, and again to another only if that consumer's process ends before acknowledging it, in no promised order. |
fipc_queue_wait_for_members | Owner: how many members of a role (or of both) are joined; waits until at least a given number are. FIPC_DISCONNECTED once the queue has ended. Any thread. |
fipc_queue_cancel | Makes every waiting and later fipc_queue_wait_for_members return FIPC_CANCELLED; the queue goes on. Any thread. |
fipc_queue_close | Ends the queue (producers get FIPC_DISCONNECTED; consumers receive what is left, then FIPC_DISCONNECTED), frees the name and the handle; the members' connections stay open until their fipc_close. |
fipc_service_create | Owner: claims a name for a service of a given ring capacity per client, longest request and reply (max_message) and numbers of clients and servers, and returns the service's handle (fipc_service_t); its process admits the members on a thread of the library's. Doesn't wait. |
fipc_service_join | Client or server (FIPC_CLIENT, FIPC_SERVER): joins the service on a name and returns its connection, waiting for the owner to create it, for a free place and to admit it. The owner joins its own service this way too, from any thread. A client's send is a request and its receive the next reply, in the order of its requests; a server's receive is the next request of any client and its send the reply. |
fipc_service_wait_for_members | Owner: how many members of a role (or of both) are joined; waits until at least a given number are. FIPC_DISCONNECTED once the service has ended. Any thread. |
fipc_service_cancel | Makes every waiting and later fipc_service_wait_for_members return FIPC_CANCELLED; the service goes on. Any thread. |
fipc_service_close | Ends the service (clients' sends and servers' receives get FIPC_DISCONNECTED; clients receive the replies already sent, then FIPC_DISCONNECTED), frees the name and the handle; the members' connections stay open until their fipc_close. |
fipc_result_str | Names a result code, such as "FIPC_OK". |
Results
fipc_result_t| Value | Code | Meaning |
|---|---|---|
| 0 | FIPC_OK | Done. |
| 1 | FIPC_TIMEOUT | The timeout ran out; with timeout 0, there was nothing to do without waiting. |
| 2 | FIPC_DISCONNECTED | The peer closed the connection or its process ended. |
| 3 | FIPC_CANCELLED | A cancel (fipc_cancel, fipc_listener_cancel, fipc_queue_cancel, fipc_service_cancel) was called, and the call would have had to wait. |
| 4 | FIPC_TOO_LARGE | The message doesn't fit the buffer, or one piece for zero-copy and on a broadcast, a queue or a service. It stays queued; its length is reported. |
| 5 | FIPC_INVALID | A bad argument or handle, a call out of order, a peer of another version or user, corrupt ring content. |
| 6 | FIPC_NO_MEMORY | Memory or another OS resource ran out while setting a connection, a broadcast, a queue or a service up. |
| 7 | FIPC_ADDR_IN_USE | fipc_stream_create, fipc_broadcast_create, fipc_queue_create, fipc_service_create: another stream, broadcast, queue or service holds the name. |
| 8 | FIPC_OVERRUN | A lossy subscriber fell a lap behind and lost messages: fipc_recv stores how many in *out_len (at least 1), and the next receive goes on from a recent message. |
| 9 | FIPC_UNSUBSCRIBED | A gated subscriber held the publisher back for the broadcast's max_stall_ms and was dropped: every later receive returns it. |
| 10 | FIPC_ABANDONED | A service's client: the server that took the request ended before it replied. The request counts as answered (*out_len is 0), and the next receive returns the next reply; it may or may not have been carried out. |