Architecture
Module Dependency Graph
xtils modules are layered. Lower-level modules have no upward dependencies:
Key relationships:
netdepends ontasks(for event loops) andsystem(for sockets)fsmdepends only onutils(json, type_traits)apporchestrates everything: owns the event loop, thread pool, and service lifecycledebug/inspectusesnetto serve a debug HTTP/WebSocket interface
Directory Layout
xtils/
├── include/xtils/ # Public headers (your #include path)
│ ├── app/ # app.h, service.h, auto-gen.h
│ ├── config/ # config.h
│ ├── debug/ # inspect.h, tracer.h
│ ├── fsm/ # fsm.h, fsm_compat.h, behavior_tree.h, bt_*logger.h
│ ├── logging/ # logger.h, sink.h, watchdog.h
│ ├── net/ # Networking
│ │ ├── transport/ # transport.h, tls_transport.h, mbedtls_transport.h,
│ │ │ # tls_factory.h, plain_tcp_transport.h
│ │ ├── http_client.h
│ │ ├── http_server.h
│ │ ├── http_router.h # Express-style routing
│ │ ├── http_multipart.h # multipart/form-data parser
│ │ ├── http_common.h
│ │ ├── tcp_client.h / tcp_server.h
│ │ ├── udp_client.h / udp_server.h
│ │ ├── websocket_client.h
│ │ └── websocket_common.h
│ ├── system/ # OS abstractions
│ ├── tasks/ # Async & scheduling
│ └── utils/ # General utilities
├── src/ # Implementation (.cc files)
├── tests/ # Unit tests (doctest)
├── examples/ # Built-in examples
├── cmake/ # CMake helpers
└── docs/ # AI-friendly documentationEvent Loop Architecture
The heart of xtils is the event loop built on Linux epoll (with poll fallback):
┌──────────────────────────────────────────┐
│ UnixTaskRunner │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────┐ │
│ │ epoll │ │ timers │ │ tasks │ │
│ │ (I/O) │ │ (heap) │ │(queue)│ │
│ └──────────┘ └──────────┘ └───────┘ │
│ │ │ │ │
│ └─────────────┴────────────┘ │
│ │ │
│ event loop tick │
└──────────────────────────────────────────┘- I/O watches: File descriptors (sockets, eventfds) trigger callbacks when readable
- Timers: Sorted by deadline; fired during each loop iteration
- Task queue: Posted work items executed in order
ThreadTaskRunner wraps a UnixTaskRunner on a dedicated thread, providing thread-safe PostTask/PostDelayedTask.
Service Lifecycle
App::Init()
│
├─ Create thread pool (TaskGroup)
├─ Create event loop (UnixTaskRunner)
├─ Load config file
├─ For each registered service:
│ ├─ Inject App* context
│ ├─ Inject service-specific config section
│ └─ Call service.Init()
│
└─ App::Run() — blocks on event loop
App::Shutdown()
│
├─ For each service (reverse order):
│ └─ Call service.Deinit() ← infrastructure still alive here!
├─ Stop thread pool
└─ Stop event loopImportant
Deinit() is called before infrastructure shutdown. This means services can still use the event loop, timers, and thread pool during cleanup (e.g., WebSocket close handshake, flushing pending I/O).
Thread Model
xtils uses a single main thread + worker pool model:
| Thread | Role | Access Pattern |
|---|---|---|
| Main thread | Event loop, I/O callbacks, timer callbacks | All net callbacks fire here |
| Worker threads | SpawnAsync tasks, CPU-bound work | Via TaskGroup |
| Service threads | ThreadTaskRunner instances | Created by services as needed |
TIP
Network callbacks always fire on the main event loop thread. Use Spawn() to post results back to main thread, or SpawnAsync() for offloading heavy work.
TLS Architecture
Consumer code
│
▼
┌─────────────────────┐
│ tls_factory.h │ ← Backend-agnostic API
└─────────────────────┘
│ │
┌─────────┘ └──────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ OpenSSL backend│ │ mbedTLS backend │
│ tls_transport │ │ mbedtls_transport│
└─────────────────┘ └─────────────────┘Both backends implement the same Transport interface. Selection is compile-time via TLS_BACKEND CMake option.
Build System Details
Compile Definitions Propagated to Consumers
| Definition | When |
|---|---|
USE_OPENSSL | TLS_BACKEND=openssl |
USE_MBEDTLS | TLS_BACKEND=mbedtls |
INSPECT_DISABLE | INSPECT_DISABLE=ON |
Exported CMake Targets
xtils— The main static library (includes all modules)- Transitively links: pthread, TLS backend libs
Source Organization
Each module has a matching src/<module>/ directory. Implementation files are .cc, one file per class/component typically.