[/
 / Copyright (c) 2003-2026 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 /
 / Distributed under the Boost Software License, Version 1.0. (See accompanying
 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 /]

[section:configuration Runtime Configuration and Concurrency Hints]

Boost.Asio provides a number of runtime configuration options that can be used to
fine tune Boost.Asio's behaviour, such as enabling or disabling specific
optimisations. The configuration options available are listed in the table
below.

[table
  [[Section][Key][Type][Default][Description]]
  [
    [`scheduler`]
    [`assume_continuation`]
    [`bool`]
    [`true` if `"scheduler"` / `"concurrency_hint"` is `1`, `false` otherwise]
    [
      When `true`, the scheduler assumes that all posted handlers represent a
      continuation of the currently executing handler, regardless of whether
      they are explicitly marked as such. Posted handlers are then added to a
      fast thread-local queue and held back until the currently executing
      handler finishes, then flushed in bulk without waking any sleeping
      threads.

      This can improve performance for short-running handlers, but the
      assumption is unsafe if a handler is a long-running and posts a handler
      that another thread is expected to pick up eagerly.
    ]
  ]
  [
    [`scheduler`]
    [`concurrency_hint`]
    [`int`]
    [`0`]
    [
      This is a suggestion to the `io_context` implementation as to the number
      of active threads that should be used for running completion handlers.

      When the Windows I/O completion port backend is in use, this value is
      passed to [^CreateIoCompletionPort].

      When a reactor-based backend is used, the implementation recognises the
      value `1` as an indication that the `io_context` will be run from a
      single thread, and applies several optimisations based on this
      assumption. For example, when a handler is posted from within another
      handler, the new handler is added to a fast thread-local queue (with the
      consequence that the new handler is held back until the currently
      executing handler finishes).

      No matter what value is specified for this configuration option, the
      `io_context` or `thread_pool` still provides full thread safety, and
      distinct I/O objects may be used from any thread.
    ]
  ]
  [
    [`scheduler`]
    [`locking`]
    [`bool`]
    [`true`]
    [
      This is used to enable or disable locking in the scheduler, when using a
      reactor-based backend. When set to `false`, this option has the following
      restrictions:

      [mdash] Care must be taken to ensure that all operations on the
      `io_context` and any of its associated I/O objects (such as sockets and
      timers) occur in only one thread at a time.

      [mdash] Asynchronous resolve operations fail with `operation_not_supported`.

      [mdash] If a `signal_set` is used with the `io_context`, `signal_set`
      objects cannot be used with any other io_context in the program.
    ]
  ]
  [
    [`scheduler`]
    [`locking_spin_count`]
    [`int`]
    [`0`]
    [
      The number of times to first attempt to acquire the scheduler's internal
      lock without blocking, when using a reactor-based backend.
    ]
  ]
  [
    [`scheduler`]
    [`task_usec`]
    [`int`]
    [`-1`]
    [
      The maximum time, in microseconds, that the scheduler will wait for its
      reactor task to complete. A value of `-1` means that no limit is placed
      on this wait time. May be set to `0` to enable CPU-bound spinning.
    ]
  ]
  [
    [`scheduler`]
    [`wait_usec`]
    [`int`]
    [`-1`]
    [
      The maximum time, in microseconds, that the scheduler will wait on its
      wake-up event in an idle thread (i.e. a thread that is not otherwise
      executing a handler or waiting on the reactor). A value of `-1` means
      that no limit is placed on this wait time. May be set to `0` to enable
      CPU-bound spinning in an execution context that is being run on multiple
      threads.
    ]
  ]
  [
    [`reactor`]
    [`preallocated_io_objects`]
    [`unsigned int`]
    [`0`]
    [
      The number of internal reactor I/O object states to allocate at
      construction.

      The reactor implementation uses per I/O object state to track things like
      the queue of outstanding operations. These state objects are recycled
      once the I/O object is destroyed, but new ones are allocated if there are
      no unused state objects currently available.

      If an upper bound on the number of I/O objects is known at construction
      time, this configuration option can be set to ensure that no allocations
      occur after construction is complete.
    ]
  ]
  [
    [`reactor`]
    [`registration_locking`]
    [`bool`]
    [`true`]
    [
      Enables or disables locking in the reactor around I/O object registration
      and deregistration.

      If set to `false`, care must be taken not to concurrently open or close
      I/O objects.
    ]
  ]
  [
    [`reactor`]
    [`registration_locking_spin_count`]
    [`int`]
    [`0`]
    [
      The number of times to first attempt to acquire the reactor's lock
      without blocking, when performing I/O object registration or
      deregistration.
    ]
  ]
  [
    [`reactor`]
    [`io_locking`]
    [`bool`]
    [`true`]
    [
      Enables or disables per I/O object locking in the reactor

      If set to `false`, care must be taken to ensure that the ['run functions]
      on the `io_context` (i.e. `run`, `run_for`, `run_until`, `run_one`,
      `run_one_for`, `run_one_until`, `poll`, and `poll_one`), and all
      operations on the context's associated I/O objects (such as sockets and
      timers), occur in only one thread at a time.
    ]
  ]
  [
    [`reactor`]
    [`io_locking_spin_count`]
    [`int`]
    [`0`]
    [
      The number of times to first attempt to acquire the reactor's per I/O
      object locks without blocking.
    ]
  ]
  [
    [`reactor`]
    [`io_uring_iowait`]
    [`bool`]
    [`true`]
    [
      Linux [^io_uring] backend only.

      When `true` (the default), a thread blocked waiting for completions is
      marked as being in [^iowait] for as long as there are pending submission
      queue entries, including those used for socket and timer waits, rather
      than genuine I/O. When `false`, this marking is suppressed via
      [^io_uring_set_iowait()], which may avoid inflated CPU utilisation
      metrics and allow the CPU to enter deeper idle states.

      Requires liburing 2.12 or later to have any effect; ignored on earlier
      versions. The underlying kernel support requires Linux kernel 6.15 or
      later.
    ]
  ]
  [
    [`reactor`]
    [`io_uring_submit_batch_size`]
    [`int`]
    [`128`]
    [
      Linux [^io_uring] backend only.

      The number of pending submission queue entries at which they are
      submitted immediately, rather than deferred and submitted together in
      a batch.
    ]
  ]
  [
    [`reactor`]
    [`io_uring_ring_count`]
    [`int`]
    [`1`]
    [
      Linux [^io_uring] backend only.

      The number of independent [^io_uring] instances ("rings") that a single
      execution context (`io_context` or `thread_pool`) maintains internally.
      Each thread that submits an operation is assigned one ring (round-robin,
      stable for the thread's lifetime).

      Increasing this value may improve scalability when the execution context
      is being used with multiple threads.

      Values that are less than one `1` are treated as `1`.
    ]
  ]
  [
    [`reactor`]
    [`io_uring_ring_size`]
    [`unsigned int`]
    [`16384`]
    [
      Linux [^io_uring] backend only.

      The number of entries used to size each [^io_uring] instance, passed to
      [^io_uring_queue_init]. Larger values allow more operations to be in
      flight before submission queue entries must be flushed, at the cost of
      more locked memory per ring. Reducing this value lowers the per-ring
      memory footprint, which can matter in environments with a low
      `RLIMIT_MEMLOCK` limit.

      A value of `0` selects the default.
    ]
  ]
  [
    [`reactor`]
    [`reset_edge_on_partial_read`]
    [`bool`]
    [`false`]
    [
      Linux [^epoll] backend only.

      When `true`, a partial read on a stream-oriented socket is considered to
      have consumed the edge-triggered readiness notification, requiring a new
      readiness edge before further read operations are performed. When `false`
      (the default), the socket is treated as ready-to-read until the read
      fails with `EAGAIN`.
    ]
  ]
  [
    [`reactor`]
    [`use_eventfd`]
    [`bool`]
    [`true`]
    [
      Linux [^epoll] backend only.

      When `true`, the reactor uses an [^eventfd] descriptor to wake a blocked
      [^epoll_wait] call. When `false`, a pipe is used instead.
    ]
  ]
  [
    [`reactor`]
    [`use_timerfd`]
    [`bool`]
    [`true`]
    [
      Linux [^epoll] backend only.

      When `true`, the reactor uses a [^timerfd] descriptor to manage timeouts.
      When `false`, the duration until the next timeout is computed and passed
      as a timeout to [^epoll_wait].
    ]
  ]
  [
    [`timer`]
    [`heap_reserve`]
    [`unsigned int`]
    [`0`]
    [
      The number of entries to reserve in the internal timer queue's heap.

      If an upper bound on the number of timers is known at construction
      time, this configuration option can be set to ensure that no timer
      allocations occur after construction is complete.
    ]
  ]
  [
    [`resolver`]
    [`threads`]
    [`unsigned int`]
    [`0`]
    [
      The number of internal threads to be used for asynchronous name
      resolution.

      If non-zero, the specified number of threads are created when the first
      resolver object is constructed. Otherwise, at most one thread is created
      at the time of the first `async_resolve` call.
    ]
  ]
]

These configuration options are associated with an execution context (such as
[link boost_asio.reference.io_context `io_context`] or [link
boost_asio.reference.thread_pool `thread_pool`]. In order to use non-default values,
a configuration service must be installed into the execution context at
construction. A number of ways to achieve this are illustrated in the sections
below.

[heading Configuration From String]

To read configuration options from a string, construct the execution context
with a [link boost_asio.reference.config_from_string `config_from_string`] object:

    boost::asio::io_context my_io_context{
        boost::asio::config_from_string{
          "scheduler.concurrency_hint=10\n"
          "scheduler.locking=1"}};

Each variable must be on a line of its own, and of the form:

    section.key=value

or, if an optional prefix is specified:

    prefix.section.key=value

Blank lines and lines starting with `#` are ignored. It is also permitted to
include a comment starting with `#` after the value.

[heading Configuration From Environment Variables]

To read configuration options from environment variables, construct the
execution context with a [link boost_asio.reference.config_from_env
`config_from_env`] object:

    boost::asio::io_context my_io_context{
        boost::asio::config_from_env{"my_app"}};

The environment variable names are formed by concatenating the prefix,
section, and key, with underscore as delimiter, and then converting the
resulting string to upper case. For example, given a prefix `"my_app"` and
the `"scheduler"` / `"concurrency_hint" option, the value is read from an
environment variable named `MY_APP_SCHEDULER_CONCURRENCY_HINT`.

[heading Configuration From Concurrency Hint]

For backwards compatibility, the [link boost_asio.reference.io_context.io_context
`io_context` constructor] can be passed a concurrency hint as an integer. This
is used to initialise the configuration options as described in the table
below.

[table
  [[concurrency_hint Value][Effect]]
  [
    [`n`, where `n < 0xFFFF`]
    [
      Equivalent to setting:

      [mdash] `"scheduler"` / `"concurrency_hint"` to `n`.

      [mdash] `"scheduler"` / `"locking"` to `true`.

      [mdash] `"reactor"` / `"registration_locking"` to `true`.

      [mdash] `"reactor"` / `"io_locking"` to `true`.
    ]
  ]
  [
    [`BOOST_ASIO_CONCURRENCY_HINT_UNSAFE`]
    [
      [mdash] `"scheduler"` / `"concurrency_hint"` to `1`.

      [mdash] `"scheduler"` / `"locking"` to `false`.

      [mdash] `"reactor"` / `"registration_locking"` to `false`.

      [mdash] `"reactor"` / `"io_locking"` to `false`.
    ]
  ]
  [
    [`BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO`]
    [
      [mdash] `"scheduler"` / `"concurrency_hint"` to `1`.

      [mdash] `"scheduler"` / `"locking"` to `true`.

      [mdash] `"reactor"` / `"registration_locking"` to `true`.

      [mdash] `"reactor"` / `"io_locking"` to `false`.
    ]
  ]
  [
    [`BOOST_ASIO_CONCURRENCY_HINT_SAFE`]
    [
      [mdash] `"scheduler"` / `"concurrency_hint"` to `0`.

      [mdash] `"scheduler"` / `"locking"` to `true`.

      [mdash] `"reactor"` / `"registration_locking"` to `true`.

      [mdash] `"reactor"` / `"io_locking"` to `true`.
    ]
  ]
]

[teletype]
The concurrency hint used by default-constructed `io_context` objects can be
overridden at compile time by defining the `BOOST_ASIO_CONCURRENCY_HINT_DEFAULT`
macro. For example, specifying

    -DBOOST_ASIO_CONCURRENCY_HINT_DEFAULT=1

on the compiler command line means that a concurrency hint of `1` is used for
all default-constructed `io_context` objects in the program. Similarly, the
concurrency hint used by `io_context` objects constructed with `1` can be
overridden by defining `BOOST_ASIO_CONCURRENCY_HINT_1`. For example, passing

    -DBOOST_ASIO_CONCURRENCY_HINT_1=BOOST_ASIO_CONCURRENCY_HINT_UNSAFE
  
to the compiler will disable thread safety for all of these objects.

[heading Custom Configuration Options]

[c++]
Applications and third-party libraries can utilise the [link
boost_asio.reference.config `config`] class to associate their own configuration
options with an execution context, or to access the configuration options
listed above. The configuration parameters' values are accessed by passing a
section, key and default value to the `get` member function:[br]

    boost::asio::config cfg{ctx};
    bool enable_locking = cfg.get("scheduler", "locking", true);

[endsect]
