# Using ASIO random_access_file with Scatter-Gather I/O

> Leverage ASIO random_access_file for efficient scatter-gather I/O. Read and write multiple buffers atomically to a specified file offset for high-performance asynchronous operations.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: tutorial
- Published: 2026-07-11

---

**ASIO's `basic_random_access_file` enables high-performance asynchronous file operations by accepting buffer sequences—collections of multiple buffers that are read or written in a single atomic operation at a specified file offset.**

The `chriskohlhoff/asio` repository provides a portable, asynchronous I/O library where scatter-gather operations allow you to transfer data between non-contiguous memory regions and disk positions efficiently. This pattern minimizes system call overhead by packing multiple buffers into a single vectored I/O operation, implemented via platform-specific services like `io_uring` on Linux and I/O Completion Ports on Windows.

## Understanding Scatter-Gather I/O in ASIO

Scatter-gather I/O refers to reading data from a single file offset into multiple memory buffers (scatter), or writing data from multiple buffers to a single file offset (gather). In ASIO, this is achieved through **buffer sequences**—types that satisfy the *ConstBufferSequence* or *MutableBufferSequence* concepts.

The `basic_random_access_file` class defined in [`include/asio/basic_random_access_file.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/basic_random_access_file.hpp) exposes member functions that accept these sequences, allowing the underlying operating system to transfer data directly into your scattered memory layout without requiring intermediate copies.

### Buffer Sequence Requirements

Any scatter-gather operation requires a buffer sequence that meets ASIO's concept requirements. Valid types include:

- `std::array<asio::mutable_buffer, N>` for fixed-size scatter operations
- `std::vector<asio::const_buffer>` for dynamic gather operations
- Custom types implementing `begin()` and `end()` returning buffer iterators

Each buffer in the sequence encapsulates a pointer and size, which the platform-specific service implementations translate into native vectored I/O structures like `iovec` (POSIX) or `WSABUF` (Windows).

## Core Asynchronous Operations

ASIO provides two layers of abstraction for scatter-gather file I/O: low-level member functions that perform single-shot transfers, and high-level free functions that guarantee complete buffer filling.

### Low-Level Member Functions

The `basic_random_access_file` class provides `async_read_some_at` and `async_write_some_at` as defined in [`include/asio/basic_random_access_file.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/basic_random_access_file.hpp). These methods initiate a single asynchronous operation that translates directly to a vectored system call:

```cpp
template<typename MutableBufferSequence, typename ReadToken>
auto async_read_some_at(uint64_t offset, 
                       const MutableBufferSequence& buffers, 
                       ReadToken&& token);

template<typename ConstBufferSequence, typename WriteToken>
auto async_write_some_at(uint64_t offset,
                        const ConstBufferSequence& buffers,
                        WriteToken&& token);

```

**Important:** These functions may transfer fewer bytes than the total size of the buffer sequence. The completion handler receives a `std::size_t` indicating the actual bytes transferred, which can be less than the sum of all buffer sizes if the operation encounters end-of-file or resource limits.

### High-Level Free Functions

For applications requiring guaranteed complete transfers, [`include/asio/read_at.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/read_at.hpp) provides `asio::async_read_at` and `asio::async_write_at`. These free functions repeatedly invoke the device's `async_read_some_at` or `async_write_some_at` until all buffers are completely filled or an error occurs:

```cpp
asio::async_read_at(file, offset, buffers, handler);
asio::async_write_at(file, offset, buffers, handler);

```

These helpers preserve ASIO's cancellation support, accepting `cancellation_type::terminal`, `partial`, or `total` to control operation lifecycle.

## Platform-Specific Implementations

Under the hood, ASIO dispatches scatter-gather requests to platform-specific service implementations that optimize the buffer sequence translation:

- **Windows:** [`include/asio/detail/win_iocp_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/win_iocp_file_service.hpp) translates buffer sequences into `WSABUF` structures for I/O Completion Port operations
- **Linux:** [`include/asio/detail/io_uring_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/io_uring_file_service.hpp) maps sequences directly to `io_uring` vectored I/O submissions for kernel-bypass efficiency

Both services implement the `async_read_some_at` and `async_write_some_at` interfaces, ensuring consistent behavior across platforms while leveraging native performance characteristics.

## Practical Code Examples

### Writing Multiple Buffers Atomically

This example demonstrates gathering data from three separate buffers into a file at offset 0:

```cpp
asio::io_context ctx;
asio::random_access_file file(ctx, "example.bin",
    asio::random_access_file::write_only |
    asio::random_access_file::create);

// Prepare three buffers.
std::array<asio::const_buffer, 3> bufs = {
    asio::buffer(data1, size1),
    asio::buffer(data2, size2),
    asio::buffer(data3, size3)};

file.async_write_some_at(0, bufs,
    [&](asio::error_code ec, std::size_t bytes_written) {
        if (!ec) {
            std::cout << "Wrote " << bytes_written << " bytes.\n";
        } else {
            std::cerr << "Write error: " << ec.message() << "\n";
        }
    });

ctx.run();

```

### Reading into Scattered Buffers with Coroutines

Using C++20 coroutines, you can scatter-read into multiple buffers with clean asynchronous syntax:

```cpp
asio::awaitable<void> read_three_buffers() {
    asio::random_access_file file(co_await asio::this_coro::executor,
                                 "example.bin",
                                 asio::random_access_file::read_only);

    std::array<char, 64> b1, b2, b3;
    std::array<asio::mutable_buffer, 3> bufs = {
        asio::buffer(b1),
        asio::buffer(b2),
        asio::buffer(b3)};

    std::size_t n = co_await file.async_read_some_at(0, bufs,
                      asio::use_awaitable);
    std::cout << "Read " << n << " bytes across three buffers.\n";
}

```

### Ensuring Complete Transfers

When your application logic requires all buffers to be filled completely, use the high-level helper instead of the low-level member function:

```cpp
asio::async_read_at(file, 0, bufs,
    [&](asio::error_code ec, std::size_t total) {
        if (!ec) {
            std::cout << "All " << total << " bytes read.\n";
        }
    });

```

## Key Considerations for Production Use

When implementing scatter-gather I/O with `random_access_file`, consider these critical aspects:

- **Cancellation:** All scatter-gather operations support per-operation cancellation. Use `asio::cancel_after` or explicit cancellation signals to abort long-running transfers
- **Partial Operations:** Always check the bytes_transferred parameter in your completion handler when using `async_read_some_at`, as short reads are normal for these low-level operations
- **Thread-Safety:** `random_access_file` objects are not copyable; use move semantics to transfer ownership between threads. The underlying service implementations ensure thread-safe completion handler dispatch
- **Offset Management:** All operations accept explicit 64-bit offsets, enabling true random access without maintaining file position state

## Summary

- **Scatter-gather I/O** in ASIO uses buffer sequences to transfer data between multiple memory regions and file offsets in a single operation
- **Low-level functions** `async_read_some_at` and `async_write_some_at` in `basic_random_access_file` provide direct access to vectored system calls
- **High-level helpers** `asio::async_read_at` and `asio::async_write_at` ensure complete buffer filling through automatic retry loops
- **Platform implementations** in [`win_iocp_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/win_iocp_file_service.hpp) and [`io_uring_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/io_uring_file_service.hpp) optimize the translation to native I/O vectors
- **Buffer sequences** must satisfy *MutableBufferSequence* or *ConstBufferSequence* concepts, typically using `std::array` or `std::vector` of ASIO buffers

## Frequently Asked Questions

### What is the difference between async_read_some_at and async_read_at?

`async_read_some_at` is a member function of `basic_random_access_file` that performs a single vectored read operation which may return fewer bytes than requested. `async_read_at` is a free function in [`read_at.hpp`](https://github.com/chriskohlhoff/asio/blob/main/read_at.hpp) that automatically loops until all buffers are completely filled or an error occurs, providing higher-level convenience at the cost of additional internal bookkeeping.

### What buffer types can I use with scatter-gather I/O?

ASIO accepts any type satisfying the *ConstBufferSequence* or *MutableBufferSequence* concepts. Common choices include `std::array<asio::mutable_buffer, N>` for fixed-size operations and `std::vector<asio::const_buffer>` for dynamic sizes. Custom types are supported if they provide `begin()` and `end()` methods returning valid buffer iterators.

### How does ASIO handle scatter-gather on Windows vs Linux?

On Windows, [`win_iocp_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/win_iocp_file_service.hpp) translates buffer sequences into `WSABUF` arrays for I/O Completion Port operations. On Linux, [`io_uring_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/io_uring_file_service.hpp) maps sequences directly to `io_uring` submission queue entries supporting vectored I/O. Both implementations perform the vectored system call atomically at the requested offset, providing equivalent semantics with platform-optimized performance.

### Is random_access_file thread-safe?

The `random_access_file` object itself is not thread-safe for concurrent operations; you must synchronize access or use strand executors. However, the object is move-only (non-copyable), allowing you to transfer ownership between threads. The underlying service implementations in [`detail/win_iocp_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/detail/win_iocp_file_service.hpp) and [`detail/io_uring_file_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/detail/io_uring_file_service.hpp) ensure that completion handlers are thread-safe when using appropriate executor configurations.