-
Notifications
You must be signed in to change notification settings - Fork 0
Services and Lifecycle
SERP applications are built from services — self-contained units of functionality that run on dedicated threads. The serp::App singleton owns all event loops, manages service registration, and coordinates the lifecycle of every service in the process.
serp::App is the process-level singleton. It:
- Creates and owns all
EventLoopinstances - Registers services and manages their dependencies
- Drives the lifecycle state machine for all services
- Blocks the main thread in
run()until shutdown is requested
Startup pattern:
int main(int argc, char** argv) {
return serp::App::instance().run(argc, argv);
}Service registration is handled in the generated main.cpp. You do not typically call App APIs directly in service code.
serp::Service is the base class for all application services. Each service is permanently bound to a named EventLoop (a single background thread). All methods and callbacks on a service execute on that thread — no additional locking is required for service-owned state.
Constructor signature:
serp::Service(serp::EventLoop& loop, std::string name);| Parameter | Description |
|---|---|
loop |
The EventLoop this service is bound to. Injected by the framework. |
name |
Human-readable name used in logs and diagnostics. |
Every service passes through five phases in order:
Created ──► Initializing ──► Initialized ──► Deinitializing ──► Deinitialized
| Phase | Description |
|---|---|
| Created | Service object has been constructed. No I/O or cross-service calls. |
| Initializing |
onInitialize() is executing. Resources may be acquired; dependencies are available. |
| Initialized | Service is fully operational. Handles calls, fires notifications, processes timers. |
| Deinitializing |
onDeinitialize() is executing. Resources should be released. |
| Deinitialized | Service is shut down. The process may exit. |
The framework guarantees:
-
onInitialize()is always called before the service handles any external requests. -
onDeinitialize()is always called before the process exits, even on graceful shutdown signals. - Both callbacks execute on the service's own
EventLoop.
Called when the framework is ready for the service to start. Use this to:
- Connect to hardware or external resources
- Register timer callbacks
- Subscribe to properties and notifications from other services
void onInitialize() override {
m_timer = serp::Timer::create(eventLoop(), std::chrono::seconds(1), [this]() {
pollStatus();
});
m_timer->start();
}Called during shutdown. Use this to:
- Stop timers
- Close connections
- Release acquired resources
void onDeinitialize() override {
if (m_timer) {
m_timer->stop();
}
}Every service is bound to exactly one EventLoop. The framework enforces this binding:
- All
serp::Callhandlers registered on a service execute on its loop. -
serp::Property::mutate()dispatches writes to the owning service's loop. -
serp::Notification::connect()delivers events on the subscriber's loop. - Timer callbacks fire on the loop they were created with.
This model eliminates data races for service-owned state without requiring explicit mutexes.
#include <serp/service.h>
class MyService : public serp::Service {
public:
MyService(serp::EventLoop& loop)
: serp::Service(loop, "MyService") {}
protected:
void onInitialize() override {
// Acquire resources, start timers, subscribe to other services
}
void onDeinitialize() override {
// Release resources, stop timers
}
private:
// Service-owned state — safe to access without locking
int m_counter = 0;
};Services are registered in the generated main.cpp. The framework resolves construction order based on declared dependencies:
// Generated main.cpp (do not edit by hand)
int main(int argc, char** argv) {
auto& app = serp::App::instance();
app.registerService<MyService>();
app.registerService<OtherService>();
return app.run(argc, argv);
}The code generator derives this file from the SerpIDL composition definition. Manual edits are overwritten on the next generation pass.
- Methods — typed inter-service calls
- Properties — shared typed state with change notification
- Notifications — one-shot event broadcasts
- Timers and Watchdog — scheduled and safety callbacks
Getting Started
The Development Model
Architecture Language
Code Generator
- Generator Overview
- Generated Code Layout
- Deployment Configurations
- Lifecycle Backends
- CMake Integration
Framework Internals
- Core Concepts
- Services & Lifecycle
- Methods
- Properties
- Notifications
- Timers & Watchdog
- Promises & Async
- Streams
- Commands
- Logging
- Test Engine
- Transports
- Runtime & Debug Tools
VS Code Plugin
Examples