Skip to main content
SerenityOS uses a type-safe, high-level IPC (Inter-Process Communication) system called LibIPC for communication between processes. This system is fundamental to the microkernel-inspired architecture, enabling services and applications to interact efficiently.

IPC Overview

LibIPC provides:
  • Type-safe message passing between processes
  • Automatic serialization/deserialization
  • Request-response and one-way messaging
  • File descriptor passing
  • Code generation from interface definitions

Architecture

The IPC system consists of several components:
┌─────────────────────────────────────────────────┐
│  Application or Service (Client)                │
├─────────────────────────────────────────────────┤
│  Generated Client Stub (ConnectionToServer)     │
├─────────────────────────────────────────────────┤
│  LibIPC (Encoder/Decoder)                       │
├─────────────────────────────────────────────────┤
│  Unix Domain Socket                             │
├─────────────────────────────────────────────────┤
│  LibIPC (Encoder/Decoder)                       │
├─────────────────────────────────────────────────┤
│  Generated Server Stub (ConnectionFromClient)   │
├─────────────────────────────────────────────────┤
│  Service Implementation                         │
└─────────────────────────────────────────────────┘

IPC Definition Language

IPC interfaces are defined in .ipc files using a simple DSL:
endpoint WindowServer
{
    // Request-response: client waits for response
    CreateWindow(Gfx::IntRect rect, String title) => (i32 window_id)
    
    // One-way message: no response expected
    InvalidateRect(i32 window_id, Gfx::IntRect rect) =|
    
    // Method with complex types
    SetWindowBitmap(i32 window_id, [Bitmap] bitmap) => ()
    
    // Get window properties
    GetWindowRect(i32 window_id) => (Gfx::IntRect rect)
}

Message Types

Request-Response Messages

Use => syntax for synchronous request-response:
GetSomething(i32 id) => (String result)
  • Client sends request and blocks waiting for response
  • Server processes request and sends response
  • Return value delivered to client

One-Way Messages

Use =| syntax for asynchronous one-way messages:
NotifySomething(String data) =|
  • Client sends message and continues immediately
  • Server processes message when received
  • No response sent back to client

Code Generation

The IPC compiler (Meta/Lagom/Tools/CodeGenerators/IPCCompiler/) generates C++ code from .ipc files:
Generated ConnectionToServer class:
// Auto-generated from WindowServer.ipc
class ConnectionToServer : public IPC::ConnectionToServer<...> {
public:
    // Synchronous call
    IPC::Messages::WindowServer::CreateWindowResponse create_window(
        Gfx::IntRect const& rect, String const& title)
    {
        return send_sync<Messages::CreateWindow>(rect, title);
    }
    
    // Asynchronous call
    void invalidate_rect(i32 window_id, Gfx::IntRect const& rect)
    {
        send<Messages::InvalidateRect>(window_id, rect);
    }
};

Serialization

LibIPC automatically serializes and deserializes messages:

Supported Types

  • bool: Boolean
  • i8, i16, i32, i64: Signed integers
  • u8, u16, u32, u64: Unsigned integers
  • float, double: Floating point
  • String: UTF-8 string
  • ByteString: Byte string
  • StringView: Non-owning view (serialize as String)
  • Vector<T>: Dynamic array
  • HashMap<K, V>: Hash map
  • Optional<T>: Maybe type
  • Any type with IPC encoding support
  • Gfx types (IntRect, IntPoint, Color, etc.)
  • File descriptors via IPC::File
  • Bitmaps via special [Bitmap] syntax

Custom Type Encoding

To make a custom type IPC-serializable:
// In YourType.h
struct YourType {
    i32 field1;
    String field2;
};

// Add encoder/decoder
namespace IPC {
    template<>
    ErrorOr<void> encode(Encoder& encoder, YourType const& value)
    {
        TRY(encoder.encode(value.field1));
        TRY(encoder.encode(value.field2));
        return {};
    }
    
    template<>
    ErrorOr<YourType> decode(Decoder& decoder)
    {
        auto field1 = TRY(decoder.decode<i32>());
        auto field2 = TRY(decoder.decode<String>());
        return YourType { field1, move(field2) };
    }
}

File Descriptor Passing

IPC supports passing file descriptors between processes:
endpoint ImageDecoder
{
    // Pass file descriptor for shared memory
    DecodeImage([File] image_file) => ([Bitmap] decoded_bitmap)
}
The [File] and [Bitmap] syntax indicates file descriptor transfer:
  • [File]: Transfers a file descriptor
  • [Bitmap]: Transfers a shared memory region containing a bitmap

Connection Lifecycle

Client Side

1

Create Connection

auto connection = TRY(WindowServer::ConnectionToServer::try_create());
2

Send Messages

// Synchronous call
auto response = connection->create_window(rect, "My Window"sv);
i32 window_id = response.window_id();

// Asynchronous call
connection->invalidate_rect(window_id, dirty_rect);
3

Handle Events

// If server sends events to client, override handlers:
void handle_paint_event(i32 window_id, Gfx::IntRect rect) override
{
    // Handle paint request from server
}

Server Side

1

Implement Connection Handler

class ClientConnection final
    : public WindowServer::ConnectionFromClient
{
public:
    virtual ~ClientConnection() override = default;
    
private:
    // Implement IPC methods
    virtual Messages::CreateWindowResponse create_window(
        Gfx::IntRect const& rect, String const& title) override
    {
        auto window_id = m_server.create_window(rect, title);
        return { window_id };
    }
    
    virtual void invalidate_rect(
        i32 window_id, Gfx::IntRect const& rect) override
    {
        m_server.invalidate_window_rect(window_id, rect);
    }
    
    Server& m_server;
};
2

Accept Connections

// Server listens on Unix socket
auto server = TRY(IPC::MultiServer<ClientConnection>::try_create());

// Event loop handles incoming connections
return event_loop.exec();

Common IPC Patterns

Service Discovery

Services listen on well-known socket paths:
// Standard service socket paths
/tmp/session/%sid/portal/window     // WindowServer
/tmp/session/%sid/portal/audio      // AudioServer
/tmp/session/%sid/portal/clipboard  // Clipboard
/tmp/session/%sid/portal/filesystemaccess  // FileSystemAccessServer

Multi-Client Services

Services handle multiple clients simultaneously:
class MyService {
public:
    void run()
    {
        // MultiServer manages multiple client connections
        auto server = TRY(IPC::MultiServer<ClientConnection>::try_create());
        
        // Each client gets own ClientConnection instance
        server->on_new_client = [this](auto& client) {
            dbgln("New client connected");
        };
        
        Core::EventLoop::current().exec();
    }
};

Request-Response with Timeout

// Client can implement timeout for sync calls
Optional<Response> send_with_timeout(Duration timeout)
{
    auto start = Time::now_monotonic();
    auto response = connection->some_request();
    
    if (Time::now_monotonic() - start > timeout)
        return {};  // Timeout
    
    return response;
}

Event Loop Integration

IPC integrates seamlessly with LibCore’s event loop:
See Event Loop Documentation for details on the event system.
// IPC messages are processed via event loop
Core::EventLoop loop;

auto connection = TRY(WindowServer::ConnectionToServer::try_create());

// Async messages don't block event loop
connection->async_invalidate_rect(window_id, rect);

// Sync messages block until response (but event loop keeps running)
auto response = connection->create_window(rect, title);

loop.exec();

Security Considerations

Unix domain sockets use filesystem permissions:
  • Sockets in /tmp/session/%sid/ are user-private
  • Only processes with same session ID can connect
  • File permissions prevent unauthorized access
Services must validate all incoming data:
virtual Messages::Response handle_request(
    i32 id, String const& data) override
{
    // Validate ID is in valid range
    if (id < 0 || id >= m_items.size())
        return Error::from_string_literal("Invalid ID");
    
    // Validate string is not too long
    if (data.length() > MAX_SIZE)
        return Error::from_string_literal("Data too large");
    
    // Process request...
}
  • Limit maximum message size
  • Limit number of concurrent connections
  • Implement rate limiting for expensive operations
  • Clean up resources when clients disconnect

Real-World Examples

WindowServer Communication

// Application creates window via IPC
auto window = GUI::Window::construct();
window->set_title("My Application");
window->resize(640, 480);
window->show();  // Internally calls WindowServer IPC

// Behind the scenes:
// GUI::Window::show() ->
//   ConnectionToWindowServer::create_window(...) ->
//   IPC message to WindowServer ->
//   WindowServer creates window ->
//   Response with window_id

ImageDecoder Service

// Decode image in isolated process
auto connection = ImageDecoderClient::Client::try_create();

// Send image data via file descriptor
auto bitmap = TRY(connection->decode_image(image_file));

// ImageDecoder runs in separate process for security
// Prevents malicious images from compromising main app

ConfigServer

// Read configuration value
auto config_client = TRY(Config::Client::try_create());
auto value = config_client->read_string("MyApp", "Settings", "theme");

// Write configuration value
config_client->write_string("MyApp", "Settings", "theme", "Dark");

// ConfigServer persists settings to disk

Performance Characteristics

IPC Performance:
  • Unix domain sockets are very fast (local communication)
  • Zero-copy for file descriptor passing
  • Minimal serialization overhead
  • Async messages have near-zero latency
  • Sync messages have ~µs round-trip time

Debugging IPC

Tools for debugging IPC issues:
# Enable IPC debug output
export IPC_DEBUG=1

# Trace IPC messages
export IPC_TRACE=1

# Monitor socket connections
ls -la /tmp/session/*/portal/

# Check for stuck connections
lsof | grep socket

Further Reading

Event Loop

How IPC integrates with the event system

Services

Learn about system services that use IPC

LibIPC Source

Explore LibIPC implementation

Example .ipc Files

Study real IPC definitions in Userland/Services/

Build docs developers (and LLMs) love