SwiftNIO Documentation

repository·main·Indexed 27 days ago

https://github.com/apple/swift-nio

A high-performance, asynchronous, event-driven network application framework for Swift, similar to Netty. It provides the building blocks for creating scalable protocol servers and clients across macOS, Linux, iOS, tvOS, and watchOS. The framework includes core modules such as NIOCore, NIOPosix, and NIOEmbedded, as well as protocol implementations for HTTP/1, WebSocket, and TLS.

Tokens
38K
Snippets
67
Records
192
Agent score
93%

What's inside SwiftNIO

  1. Overview of _NIOFileSystem

    main

    NIOFileSystem is a cross-platform file system library for Swift. It provides a concrete FileSystem implementation for interacting with the local file system and a set of protocols for building custom file system implementations.

    Platform Considerations:

    • Feature Parity: Platforms may not have identical system-level API parity (e.g., file cloning on Apple platforms).
    • Feature Availability: Some features, such as extended attributes, may be disabled on certain systems.
    • Path Representations: File paths vary by platform (e.g., POSIX-style "/Users/hal9000/" on Apple/Linux vs. Windows-style "C:\Users\hal9000" on Windows).
    • File Information: FileInfo representations differ depending on the underlying platform.
  2. Overview of SwiftNIO

    main
    SwiftNIO is a cross-platform, asynchronous, event-driven network application framework designed for the rapid development of high-performance, maintainable protocol servers and clients. It is the Swift equivalent of the Netty framework.
  3. Understand the foundational types for Async NIO bridging

    main

    The async bridging in SwiftNIO relies on two foundational types to manage data flow and back-pressure between the Channel and Swift Concurrency:

    • NIOAsyncSequenceProducer: Bridges the read side of a Channel to an AsyncSequence.
    • NIOAsyncWriter: Bridges the write side of a Channel to an async interface.

    These types are used internally by NIOAsyncChannel to ensure that the asynchronous consumer does not overwhelm the event loop and vice versa.

  4. Understand SwiftNIO Core Architecture

    main

    SwiftNIO is a low-level, non-blocking I/O networking framework. It is built around 8 core types provided by NIOCore that form the foundation of all SwiftNIO applications:

    1. EventLoopGroup (Protocol): A collection of event loops used to distribute work.
    2. EventLoop (Protocol): The basic I/O primitive that waits for events and dispatches callbacks.
    3. Channel (Protocol): Represents a file descriptor and manages its lifetime and I/O events.
    4. ChannelHandler (Protocol): Objects in a pipeline that process inbound or outbound events.
    5. Bootstrap (Structures): High-level abstractions used to streamline the creation of channels.
    6. ByteBuffer (Struct): A high-performance, copy-on-write byte buffer for data manipulation.
    7. EventLoopFuture<T> (Generic Class): A container for a value that will be available asynchronously.
    8. EventLoopPromise<T> (Generic Struct): The object used to fulfill an EventLoopFuture.
  5. SwiftNIO Repository Organization and Modules

    main

    The SwiftNIO ecosystem is distributed across several repositories. The main apple/swift-nio repository contains several key modules:

    • NIO: An umbrella module exporting NIOCore, NIOEmbedded, and NIOPosix.
    • NIOCore: Provides core abstractions and types. Most extension projects (new EventLoops or Channels) should depend only on NIOCore.
    • NIOPosix: Provides the high-performance I/O layer, including EventLoopGroup, EventLoop, and Channels for POSIX-based systems. Use this for actual I/O operations.
    • NIOEmbedded: Provides EmbeddedChannel and EmbeddedEventLoop for fine-grained control, primarily used for testing or decoupled protocol driving.
    • NIOConcurrencyHelpers: Low-level concurrency primitives like locks and atomics.
    • NIOFoundationCompat: Enables interoperation between NIO types and Foundation data types (e.g., Data).
    • NIOTLS: Provides abstraction types for working with multiple TLS implementations (does not provide TLS itself).
    • NIOHTTP1: Low-level HTTP/1.1 protocol implementation.
    • NIOWebSocket: Low-level WebSocket protocol implementation.
    • NIOTestUtils: Helpers for testing SwiftNIO-based projects.
    • _NIOFileSystem: Provides async APIs for file system interaction.
  6. Understand SwiftNIO Module Organization

    main

    SwiftNIO is organized into several specialized modules. Depending on your needs, you may need to depend on one or more of the following:

    • NIOCore: The fundamental abstraction layer. Most extension projects (new EventLoops or Channels) should only depend on this.
    • NIOPosix: The high-performance I/O layer for POSIX-based systems. Import this for actual I/O, such as MultiThreadedEventLoopGroup and SocketChannel.
    • NIOEmbedded: Provides EmbeddedChannel and EmbeddedEventLoop for fine-grained control, primarily used for testing or decoupled protocol driving.
    • NIOFoundationCompat: Use this to interoperate NIO types with Foundation types like Data.
    • NIOConcurrencyHelpers: Provides low-level concurrency primitives like locks and atomics.
    • NIOHTTP1: Low-level HTTP/1.1 protocol implementation.
    • NIOWebSocket: Low-level WebSocket protocol implementation.
    • NIOTestUtils: Helpers for testing SwiftNIO-based projects.
    • _NIOFileSystem: Provides async APIs for file system interaction.
  7. Use NIOPosix for POSIX-compliant networking

    main
    Use the NIOPosix module to access concrete implementations of SwiftNIO abstractions on POSIX-compliant operating systems such as Linux, macOS, and other Unix-like platforms. It provides platform-specific channel implementations, event loops, and networking features designed to leverage operating system capabilities for high-performance networking.
  8. Collect allocation traces with bpftrace (Linux)

    main

    On Linux, use the malloc-aggregation.bt script (found in the dev directory) to collect allocation stacks.

    Installation (Ubuntu):

    apt-get update && apt-get install -y bpftrace

    Usage: You can run the script by passing the executable path with the -c flag. However, due to symbolication limitations in bpftrace when tracing existing processes, it is recommended to use PID-based tracing with the -p flag.

    To run your executable in the background and immediately attach the script to its PID, use the following pattern:

  9. Understand allocation counter test output

    main

    The output of ./run-nio-alloc-counter-tests.sh provides metrics for each test case. To analyze a regression, look at the following fields for each test file:

    • remaining_allocations: If this is 0, the test did not leak memory. A non-zero value indicates a leak.
    • total_allocations: The total number of allocations observed across all runs. To find the allocations per single run, divide this number by the number of runs (typically 1000). Ignore small amounts of 'noise' (e.g., 1 extra allocation) caused by Swift runtime initialization.
    • total_allocated_bytes: The total number of bytes allocated by the test.
    • DEBUG: [...]: A list of the exact metrics for each individual run. Use this to check for determinism. If the numbers fluctuate significantly between runs, the test is not allocating deterministically.
    test_future_lots_of_callbacks.remaining_allocations: 0
    test_future_lots_of_callbacks.total_allocations: 75001
    test_future_lots_of_callbacks.total_allocated_bytes: 4138056
    DEBUG: [["remaining_allocations": 0, "total_allocations": 75001, "total_allocated_bytes": 4138056], ...]
  10. Schedule operations on an EventLoop using NIOIsolatedEventLoop

    main

    If you need to schedule operations on the EventLoop where your code is currently executing, you can use EventLoop.assumeIsolated() (available in NIO 2.77.0+). This returns a NIOIsolatedEventLoop, which is a non-Sendable view that allows you to drop @Sendable requirements on operation closures and many Sendable requirements on return types.

    Critical Safety Warnings:

    • assumeIsolated() supplements compile-time checks with runtime ones. If isolation domains do not align, your code may crash.
    • Do not call assumeIsolated() from a Swift concurrency context (e.g., an async method or from within an actor). It must be called from a context that is already on the EventLoop to verify the isolation domain.
    • If you are unsure if an EventLoopFuture is bound to your current EventLoop, use EventLoopFuture.hop(to:) to move it to your isolation domain before proceeding.