CloudtoidCloudtoid / interprocess

.NET API reference

Send ReadOnlySpan<byte> messages and receive into reusable memory. Use CancellationToken when waiting for work.

NuGet package ↗ · Public contracts ↗

Install

Requires .NET 10 or later and a 64-bit process on a supported platform.

dotnet add package Cloudtoid.Interprocess

Send and receive

using Cloudtoid.Interprocess;

var options = new QueueOptions("example", capacity: 65536);
var factory = new QueueFactory();
using var subscriber = factory.CreateSubscriber(options);
using var publisher = factory.CreatePublisher(options);

if (!publisher.TryEnqueue("hello"u8))
    throw new InvalidOperationException("Queue is full or recovering");

byte[] buffer = new byte[256];
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(1));
ReadOnlyMemory<byte> message = subscriber.Dequeue(buffer, cancellation.Token);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(message.Span));

QueueOptions

QueueOptions(string queueName, long capacity)

QueueOptions(string queueName, string path, long capacity)

Immutable queue configuration. The first overload uses Path.GetTempPath(). Windows ignores the backing path. Capacity is message-buffer bytes, greater than 16 and divisible by 8. Read-only properties are string QueueName, string Path, and long Capacity.

QueueFactory and IQueueFactory

QueueFactory() / QueueFactory(ILoggerFactory loggerFactory)

Constructs the factory with default logging or your own logger factory.

IPublisher CreatePublisher(QueueOptions options)

ISubscriber CreateSubscriber(QueueOptions options)

Create or join the transient queue as a publisher or subscriber. Both returned interfaces implement IDisposable. At most 2,048 publisher objects may be connected to a queue.

IServiceCollection AddInterprocessQueue(this IServiceCollection services)

Registers IQueueFactory with the service collection. Call services.AddInterprocessQueue() and resolve IQueueFactory through dependency injection.

IPublisher

bool TryEnqueue(ReadOnlySpan<byte> message)

Copies the bytes into shared memory without waiting for space. Returns false when the message does not fit or recovery temporarily closes admission; true means the message was committed. A notification semaphore reaching its limit does not fail an already committed send.

ISubscriber

bool TryDequeue(out ReadOnlyMemory<byte> message)

Tries once, allocating a byte array for a received message. False means no ready message, an active reader owns consumption, or the next message is unfinished.

bool TryDequeue(Memory<byte> buffer, out ReadOnlyMemory<byte> message)

Copies into your buffer. On success the returned memory references the filled part of that buffer; consume it before reusing or modifying the buffer. An undersized buffer truncates and consumes the message. A true result with zero length is a real empty message.

ReadOnlyMemory<byte> Dequeue(CancellationToken cancellation)

Blocks until a message arrives, cancellation is requested, disposal is observed, or a failure occurs. Allocates a result array. Use CancellationToken.None to wait without application cancellation.

ReadOnlyMemory<byte> Dequeue(Memory<byte> buffer, CancellationToken cancellation)

Blocking receive into caller-owned storage, with the same truncation and buffer-lifetime rules as TryDequeue with a buffer.

Failures and cancellation

Cancellation and subscriber disposal throw OperationCanceledException from receive operations. Sending through a disposed publisher throws ObjectDisposedException. Invalid options are rejected by argument validation. A publisher-limit failure throws InvalidOperationException; counter exhaustion throws OverflowException. Queue opening and access can also fail because of capacity mismatch, permissions, I/O, or invalid shared state.

False from TryEnqueue/TryDequeue is not an exception. Apply a bounded retry policy if the application needs to wait. TryDequeue has no cancellation token because it does not wait for a message.

Cleanup and concurrency

Use using for each endpoint. Disposal stops new calls and waits for admitted calls to finish before releasing resources; waiting readers observe disposal on retry. Consumption is serialized across subscribers. A stalled live participant is not treated as crashed simply because a deadline passes.

The API is synchronous; there is no DequeueAsync or batch-enqueue API. Reusing receive buffers avoids per-message result-array allocation. See queue lifetime before handing off between processes.