modern-runtime is a modular C++23 runtime for applications and libraries that need schedulers, thread pools, timers, tasks, coroutine-aware execution, and allocator-friendly async composition. It provides a reusable execution foundation so you can build async systems on top of a consistent runtime layer instead of re-solving scheduling, timing, cancellation, continuation chaining, and completion handoff in each project.
Use modern_runtime when you want to:
- run CPU work, timer work, and completion-based async work through one runtime surface
- compose pipelines with
modern::task<T>using continuations (then,catching,finally) or coroutines (co_await,co_return) - propagate scheduler, allocator, stop, and trace context across async boundaries
- keep execution concerns separate from domain code such as networking, file handling, or protocol layers
- integrate external I/O backends without rebuilding a full runtime around them
modern_runtime consumes the shared trace contract from modern_trace. Runtime-owned behavior is propagation and environment seeding, not trace wire-format parsing or drain infrastructure.
modern_runtime is the layer below domain-specific async libraries. It owns scheduling, task orchestration, timers, coroutine task infrastructure, PMR-aware allocation points, and runtime adapters. It intentionally does not try to become a second filesystem or networking framework next to modern_io.
modern.exec: schedulers, executor primitives, and priority-aware executionmodern.thread: thread pools, work stealing, bounded queues, and affinity-aware workersmodern.timer: delayed and periodic schedulingmodern.task:task<T>continuations withthen,catching, andfinallymodern.stream: bounded, backpressured, single-producer/single-consumer streamsmodern.runtime: umbrella module for adapters, fibers, senders, and coroutine-native runtime tasksmodern.memory: a smallstd::pmr-oriented allocation surfacemodern.syncandmodern.platform: synchronization and platform helpers used by the runtime surface
Requirements:
- C++23 toolchain
- CMake 3.28+
- Clang with module support is the primary validated setup
- Ninja is used by the included presets
Debug build:
# Source builds resolve modern_trace automatically:
# sibling ../modern_trace when present, otherwise GitHub fetch.
# Override with -DMODERN_TRACE_PROVIDER=package|local|fetch
# and -DMODERN_TRACE_ROOT=../modern_trace.
cmake --preset debug
cmake --build --preset debug
./build/debug/exec_smoke
./build/debug/async_demo
./build/debug/smoke_testOther presets:
cmake --preset release
cmake --build --preset release
cmake --preset sanitize
cmake --build --preset sanitizeThe local validated setup is Clang 18.1.3, Ninja, CMake 3.28.3, and libstdc++ 13. GCC 13 is not an active validation target at the moment.
The repository builds a modern_runtime library target and resolves modern_trace as a sibling or fetched dependency.
add_subdirectory(modern_runtime)
target_link_libraries(my_app PRIVATE modern_runtime)If you want the full umbrella surface, import modern.runtime. If you want tighter compile-time boundaries, import only the modules you need.
import modern.runtime;
#include <chrono>
#include <iostream>
#include <string>
int main()
{
using namespace std::chrono_literals;
modern::thread_pool cpu{4};
modern::scheduled_executor timers{cpu};
auto value = modern::submit(cpu, []
{
return 21;
})
.then([](int x)
{
return x * 2;
})
.get();
auto delayed = modern::schedule_after(timers, 100ms, []
{
return std::string{"done"};
})
.then([](std::string text)
{
return text + " after a timer";
})
.get();
std::cout << value << "\n";
std::cout << delayed << "\n";
timers.shutdown();
timers.join();
cpu.shutdown();
cpu.join();
}That is the core usage pattern:
- create an execution context such as
thread_poolorscheduled_executor - submit work with
modern::submit(...) - compose follow-up work with continuations or coroutines
- wait, stop, or hand results onward at your application boundary
Use the umbrella when you want the complete runtime layer:
import modern.runtime;Use narrow imports when you want explicit boundaries:
import modern.exec;
import modern.task;
import modern.stream;
import modern.timer;
import modern.thread;
import modern.sync;
import modern.memory;
import modern.platform;modern::task<T> is the single unified task type. It supports both composition styles:
Continuation style:
auto result = modern::submit(pool, work)
.then(transform)
.catching(handle_error)
.finally(cleanup);Coroutine style:
modern::task<int> operation()
{
auto value = co_await modern::submit(pool, work);
co_return value * 2;
}Mixed style:
modern::task<int> operation()
{
auto value = co_await modern::submit(pool, work)
.then([](int x) { return x * 2; });
co_return value;
}All one-shot async producers (submit, schedule_after, schedule_at, bind_io, as_task, I/O adapters) return modern::task<T>.
Start semantics:
- Coroutine functions returning
task<T>start eagerly (run immediately on creation) submit,schedule_after,schedule_at, I/O operations start eagerly (work submitted/armed immediately)
Consumption:
task<T>is move-only and single-consumer- Consume via
get(),co_await, orthen()/catching()/finally() - Second consumption throws
std::logic_error
Cancellation:
std::stop_tokenpropagates throughtask_environmentco_awaitandget()throwoperation_cancelledon stopped state
modern::submit(...)runs work on a scheduler or thread pool and returns a task you can compose.modern::schedule_after(...)andmodern::schedule_at(...)run delayed work.scheduled_executor::schedule_fixed_rate(...)is the periodic entry point for repeated jobs.
thread_poolandscheduled_executorcan usestd::pmr::memory_resourcefor runtime allocations.- task continuations can keep propagating a parent resource or accept an explicit override.
- runtime adapters accept
std::stop_tokenso cancelled work can fail early before execution starts. - schedulers support
modern::scheduler_prioritywithhigh,normal, andlow. - thread pools can pin workers to configured CPU affinities.
modern::bind_io<R>(...)adapts completion-based I/O starts intotask<R>.modern::adapt_modern_io(service)wraps modern_io-like services that exposestart(...)orsubmit(...).modern::adapt_modern_io_uring(service)andmodern::adapt_modern_io_poller(service)add backend-aware integration forio_uring,epoll, andkqueuestyle services.
This keeps the ownership line clear: modern_runtime provides the runtime substrate, while modern_io or another domain layer owns file, socket, and transport semantics.
modern::as_task<R>(sender, scheduler, resource)adapts senders intotask<R>.modern::fiber_schedulerprovides cooperative scheduler-based fiber requeueing and grouped stop/join behavior.modern::task_environmentcarries scheduler, frame allocator, trace context, and stop state through coroutine and continuation execution.
modern::stream<T> is a move-only, bounded SPSC stream. next() returns a
task containing either a value, clean EOF, or a typed stream_error. Producers
created through make_stream() receive a move-only stream_source<T>; its
send() operation applies backpressure while the configured buffer is full.
auto values = modern::make_stream<int>(1,
[](modern::stream_source<int> source) -> modern::task<void>
{
if (auto sent = co_await source.send(1); !sent)
co_return;
if (auto sent = co_await source.send(2); !sent)
co_return;
});
while (auto item = values.next().get())
{
if (!*item)
break;
std::cout << **item << "\n";
}Normal producer completion closes and drains accepted values. Failure and cancellation discard buffered values immediately. The complete contract is in docs/stream_contract.md.
- modules/exec/modern.exec.cppm: scheduler, priorities, and inline executor
- modules/task/modern.task.cppm:
task<T>,task_scope, continuations, coroutine support, and environment - modules/stream/modern.stream.cppm: bounded SPSC stream core, producer source, and completion observation
- modules/thread/modern.thread.cppm: thread pool with work stealing, affinity, and prioritized scheduling
- modules/timer/modern.timer.cppm: delayed and periodic scheduling
- modules/runtime/fiber_scheduler.cppm: cooperative fiber scheduler over the scheduler surface
- modules/runtime/io_bridge.cppm: bridge from external completion sources to
task<T> - modules/runtime/io_backend.cppm: backend metadata and abstraction for
io_uring,epoll, andkqueue - modules/runtime/modern_io_adapter.cppm: concrete shim for modern_io-like services over
bind_io - modules/runtime/io_uring_adapter.cppm:
io_uring-specific adapter over the modern_io shim - modules/runtime/poller_adapter.cppm: shared
epoll/kqueueabstraction over the modern_io shim - modules/runtime/sender_bridge.cppm: bridge from senders to
task<T> - modules/memory/modern.memory.cppm: small
std::pmrsurface - modules/modern.runtime.cppm: umbrella module and integration functions
- examples/async_demo.cpp: runnable reference demo