Skip to content

.NET inter process broadcast message bus with supporting classes

License

Notifications You must be signed in to change notification settings

steamcore/TinyIpc

Repository files navigation

TinyIpc

NuGet Build

TinyIpc is a lightweight, serverless .NET inter-process broadcast message bus designed for simplicity and performance in Windows desktop applications.

Table of Contents

Features

  • Serverless Architecture: No master process required.
  • Flexible Messaging: Clients can join or leave at any time.
  • Automatic Expiration: Messages expire after a configurable timeout (default: 1 second).
  • Memory Efficient: Default max log size is 1 MB for high performance.
  • FIFO Guarantee: Messages are received in the order they are published.
  • Fully async: Supports receiving events in callbacks or via async enumerable

Benefits and Limitations

Benefits

  • Easy to set up, no complex configurations required.
  • Ideal for small, quick messages.
  • Fully in-memory for high-speed communication.
  • Can batch send many messages in one operation.
  • Broadcast messages to all listeners (except the sender).

Limitations

  • Windows only: Relies on named primitives unavailable on other platforms.
  • Message size: Not suitable for large payloads.
  • Timeout sensitivity: High throughput can lead to message loss if receivers fail to acquire locks in time.

Performance

Every publish operation reads and writes the entire shared memory mapped file, and every receive operation which is triggered after writes also reads the entire file.

Thus, if high throughput is desired, batch publish several messages at once to reduce I/O operations.

OS Support

TinyIpc currently supports Windows only due to reliance on platform-specific primitives.

For more details, refer to this issue.

Feature Comparison

TinyIPC IpcChannel Named Pipes
Broadcasting to all listeners (except self)
Serverless architecture
Process privilege agnostic
Fully in-memory

Examples

Simple Example

Check ConsoleApp for a sample application.

using var messagebus1 = new TinyMessageBus("ExampleChannel");
using var messagebus2 = new TinyMessageBus("ExampleChannel");

messagebus2.MessageReceived +=
	(sender, e) => Console.WriteLine(e.Message.ToString());

while (true)
{
	var message = Console.ReadLine();
	await messagebus1.PublishAsync(BinaryData.FromString(message));
}

Generic Hosting Example

Check GenericHost for a sample application.

// Add a reusable ITinyMessageBus
services.AddTinyMessageBus(options =>
{
	options.Name = "ExampleChannel";
});

// Then use ITinyMessageBus via dependency injection
public class SomeService(ITinyMessageBus tinyMessageBus)
{
	public Task PublishMessage(string message)
	{
		return tinyMessageBus.PublishAsync(BinaryData.FromString(message));
	}

	public async Task Subscribe()
	{
		await foreach (var message in tinyMessageBus.SubscribeAsync())
		{
			Console.WriteLine(message.ToString());
		}
	}
}

Advanced Usage

You can also add keyed instances with different settings.

services.AddKeyedTinyMessageBus("instance1", options =>
{
	options.Name = "Channel1";
});

services.AddKeyedTinyMessageBus("instance2", options =>
{
	options.Name = "Channel2";
});

// Then resolve them using [FromKeyedServices(serviceKey)] or via a IServiceProvider

Or you might need to use dependency injection and create multiple instances communicating with the same message bus in the same application.

// Add a factory service to IServiceCollection
services.AddTinyIpcFactory(options =>
{
	options.Name = "ExampleChannel";
});

// Later use ITinyIpcFactory to create instances
using var tinyIpcInstance1 = tinyIpcFactory.CreateInstance();
using var tinyIpcInstance2 = tinyIpcFactory.CreateInstance();

tinyIpcInstance2.MessageBus.MessageReceived +=
	(sender, e) => Console.WriteLine(e.Message.ToString());

while (true)
{
	var message = Console.ReadLine();
	await tinyIpcInstance1.MessageBus.PublishAsync(BinaryData.FromString(message));
}