A header-only C++23 collection of thread-safe, high-performance primitives —
concurrency building blocks (queues, caches, pools, a Bloom filter) and a
networking stack (reactor + poller + HTTP handler). The library is the
collection of muses/*.hpp headers; an HTTP server built on top of it lives in
examples/ as a demonstration of how the primitives compose.
This is a learning/portfolio project. Every component is independently tested
under AddressSanitizer + UBSan. C++23 facilities are used where they help:
std::format, std::flat_map, std::expected, std::span, std::counting_semaphore.
General-purpose primitives (muses/):
| Component | Header | Notes |
|---|---|---|
| Thread pool | thread_pool.hpp |
fixed-size, futures, wait_empty() |
| Bounded queue | bounded_queue.hpp |
lock-free Vyukov MPMC ring (DropNew/DropOldest); mutex+deque for Block |
| Unbounded queue | queue.hpp |
blocking push/pop, retained for general use |
| Memory pool | memory_pool.hpp |
size-class free-lists + per-alloc refcount + PoolPtr<T> + PooledBuffer |
| LRU cache | lru_cache.hpp |
O(1) get/put/erase, mutex-locked, entry-count bounded |
| Counting Bloom filter | bloom_filter.hpp |
removable, decay-based expiry, mutex-locked |
| Coroutine task | task.hpp |
Task<T> + IoAwaiter; Poller is the scheduler |
| Logger | logging.hpp |
async, single background thread, bounded DropOldest ring |
| Profiler | profiler.hpp |
RAII scope timer (micro-benchmark tool) |
Networking stack (muses/net/):
| Component | Header | Notes |
|---|---|---|
| Poller | net/poller.hpp + *_poller.hpp |
edge-triggered kqueue/epoll abstraction + wakeup() |
| Reactor | net/reactor.hpp |
sharded (SO_REUSEPORT) reactor pool + async writes + ReactorPool |
| HTTP handler | net_components/http_handler.hpp |
static files, CRLF, keep-alive, traversal-safe, LRU-cached |
| Reverse proxy | net_components/proxy.hpp |
coroutine-driven, prefix routing, upstream pool, retry, health |
Requires CMake >= 3.18 and a C++23 compiler (clang >= 17 / gcc >= 14; libc++ or
libstdc++ with std::flat_map, std::expected, std::format, std::span).
cmake -S . -B build
cmake --build build -jDebug builds enable AddressSanitizer + UndefinedBehaviorSanitizer by default.
Disable with -DMUSES_ENABLE_SANITIZERS=OFF.
Tests use doctest, fetched via
FetchContent (no manual install). Each tests/test_*.cpp is a standalone
executable registered with CTest. 13 suites, all green under ASan/UBSan.
cd build && ctest --output-on-failureThe reactor and poller tests bind real sockets on 127.0.0.1 ephemeral ports.
Static-file HTTP server (examples/http_server.cpp) serving the bundled
tests/statics/index.html (11 KB) over HTTP/1.1 keep-alive. Measured with
wrk on Linux x86_64 (8 cores), Release build
(-O3 -march=native -flto, sanitizers off), N=reactor shards = hardware_concurrency().
| Concurrency | req/s | bandwidth | p50 | p99 |
|---|---|---|---|---|
| 1 conn | 9.0k | 92 MB/s | 89 µs | 1.62 ms |
| 50 | 155k | 1.58 GB/s | 256 µs | 1.74 ms |
| 200 | 170k | 1.72 GB/s | 1.00 ms | 4.66 ms |
| 500 | 199k | 2.02 GB/s | 2.10 ms | 7.86 ms |
| 1000 | 186k | 1.89 GB/s | 4.51 ms | 16.98 ms |
Peak throughput: ~199k req/s at ~2 GB/s, saturating ~4 cores (out of 8)
under wrk -t8 -c500. RSS stays around 2 MB at low concurrency, ~40 MB at
c=1000. p99 stays under 20 ms across the sweep — the per-fd sharding and the
lock-free outbox keep tail latency flat under load.
For comparison, the same binary built with Debug + ASan + UBSan (the default)
hits ~38–42k req/s — i.e. enabling sanitizers costs ~4–5× throughput, which
matches the conventional ASan overhead range.
cmake -S . -B build-release \
-DCMAKE_BUILD_TYPE=Release \
-DMUSES_ENABLE_SANITIZERS=OFF \
-DCMAKE_CXX_FLAGS="-O3 -march=native -flto" \
-DCMAKE_EXE_LINKER_FLAGS="-flto"
cmake --build build-release -j
cd build-release && MUSES_PORT=8865 ./muses_http_server &
wrk -t8 -c500 -d10s --latency http://127.0.0.1:8865/A demo built from the primitives — ReactorPool (multi-reactor) +
HttpContext (static files) + ThreadPool (workers). It is an example of
using the library, not the library's purpose; the components above are.
cd build && ./muses_http_server
# serves ./statics on http://127.0.0.1:8864/
# MUSES_REACTORS reactor shards (default: hardware_concurrency())
# MUSES_WORKERS worker threads per shard (default 4)
# MUSES_PORT listen port (default 8864)#include "muses/net/reactor.hpp"
#include "muses/net_components/http_handler.hpp"
muses::TCPListener listener("127.0.0.1", port);
int lfd = *listener.get_listener(); // SO_REUSEPORT enabled
muses::ReactorPool pool(lfd,
[](const std::string& req) -> muses::HandlerResult {
auto hr = muses::HttpContext::handle_request(req);
return {std::move(hr.response), hr.keep_alive};
}, /*shards=*/4, /*workers_per_shard=*/2);
pool.start();A second demo, built on the coroutine infrastructure: each client connection
is a Task<void> that forwards requests to a matched upstream over pooled
keep-alive connections, with retry and health gating. Upstream I/O suspends on
the Poller instead of blocking a worker — this is what task.hpp enables.
cd build && ./muses_reverse_proxy
# listens on http://127.0.0.1:8865/
# MUSES_PROXY_PORT listen port (default 8865)
# Edit the hardcoded route table in examples/reverse_proxy.cpp and rebuild
# to route to your backends.Bounded queue (bounded_queue.hpp): DropNew/DropOldest use a lock-free
Vyukov MPMC bounded ring — per-slot atomic sequence counters, cache-line-padded
producer/consumer cursors, counting_semaphore for blocking consumers (decoupled
from the data path, so try_push/try_pop stay lock-free). Block retains the
proven mutex+deque implementation. This is the logging backend and the reactor
outbox.
Memory pool (memory_pool.hpp): every allocation is prefixed with a
BlockHeader carrying a magic cookie, the owning size-class index, and an
atomic reference count. Allocations are served from per-class free-lists backed
by a pre-allocated arena; oversized requests fall back to ::operator new and
are tagged ESCAPED. PoolPtr<T> is the RAII handle (copy = retain, move =
transfer, destroy = release). PooledBuffer is a growable byte buffer backed by
the pool, reused across keep-alive requests on a connection.
LRU cache (lru_cache.hpp): a doubly-linked list (std::list) ordered
MRU→LRU paired with an unordered_map from key to list iterator. Both splice
and iterator stability are O(1), so get/put/erase are O(1). Mutex-locked for
open-box thread safety.
Counting Bloom filter (bloom_filter.hpp): 4-bit packed counters support
removal (unlike a standard Bloom) and decay-based expiry (right-shift all
counters periodically) instead of per-entry TTLs — matching rate-limiter /
blacklist semantics without a hashmap. Double-hashing derives k hashes from
two base hashes.
Logger (logging.hpp): producers push entries into a bounded MPMC ring
(DropOldest, so the hot path is never blocked). A single background thread
drains in batches and writes to file. wait_pop(timeout) + stop() in the
destructor guarantee no deadlock on shutdown.
Reactor (net/reactor.hpp): a sharded reactor pool — N independent reactor
threads share one listen fd via SO_REUSEPORT (the kernel hands each connection
to one shard, which owns the fd for its lifetime). Each shard runs an
edge-triggered poller (kqueue EV_CLEAR / epoll EPOLLET), accepts, reads, and
dispatches complete requests to a private worker pool. Workers compute the
response and post it back through a bounded lock-free outbox; the reactor does
the non-blocking write, parking partial writes on the connection and arming
writable interest to drain later. Interest changes are batched into a single
changelist per loop iteration. Handback routing needs no cross-shard locking
because each fd lives on exactly one shard.
Poller (net/poller.hpp): EventMask bit flags + PollEvent{fd,mask,userdata}
abstract edge-triggered kqueue/epoll. wakeup() (EVFILT_USER on macOS, eventfd
on Linux) lets another thread interrupt a blocked wait().
- HTTP/1.1 only; no HTTPS, WebSocket, HTTP/2, routing, or middleware.
- Static-file serving and a coroutine-driven reverse proxy are provided as examples; neither does TLS/HTTPS, HTTP/2, or WebSocket.
- DoS hardening: idle-timeout (slowloris defense), max-connections cap, and
per-IP connection-rate limiting (sliding window +
CountingBloomFilterblacklist) are implemented and configurable via env vars (MUSES_IDLE_TIMEOUT,MUSES_MAX_CONNECTIONS,MUSES_IP_RATE; all default to safe/off). There is no request-body rate limiting or authentication. - Edge-triggered I/O means the read path must drain to EAGAIN and re-check buffered pipelined bytes after each response — handled, but it is the kind of code that is easy to regress; the pipelined-request test guards it.
- CI was removed (GitHub runners lack a new-enough C++23 toolchain); tested locally on Apple clang / libc++ (Darwin 25) and GCC 15 / libstdc++ (Linux x86_64, kernel 7.0). Both the kqueue and epoll backends are exercised by the test suite and the demo servers.
- Windows / IOCP is not supported.
MIT.