How to Use GDaemon for Background Tasks in CGraph: A Complete Guide
Use GDaemon for background tasks in CGraph by inheriting from the GDaemon base class, overriding the daemonTask method, and registering the instance with GPipeline::addGDaemon(intervalMs).
The CGraph repository provides a dedicated daemon subsystem that enables periodic background execution alongside your main pipeline processing. Unlike standard graph nodes that execute within the dataflow, GDaemon instances run on independent timer threads, making them ideal for monitoring, heartbeat signals, or simulated I/O operations.
What Is GDaemon in CGraph?
GDaemon is an abstract base class defined in src/GraphCtrl/GraphDaemon/GDaemon.h that encapsulates a lightweight, timer-driven background worker. It is part of a three-layer architecture:
GDaemon– The abstract interface you subclass. It implements timer handling and declares the pure virtualdaemonTaskmethod.GDaemonObject– Defined insrc/GraphCtrl/GraphDaemon/GDaemonObject.h, provides common utilities including parameter managers and interval setters.GDaemonManager– Implemented insrc/GraphCtrl/GraphDaemon/GDaemonManager.cpp, owns daemon instances and manages theirinit()anddestroy()lifecycle.
The subsystem relies on UTimer, an internal utility that spawns a separate thread to trigger your task at a configurable millisecond interval.
GDaemon Architecture and Lifecycle
Core Components
The daemon system integrates tightly with the pipeline execution model. When you call addGDaemon, the pipeline (specifically the __addGDaemon_4py method in src/GraphCtrl/GraphPipeline/GPipeline.cpp) performs three critical actions:
- Instantiates your daemon template.
- Invokes
setInterval(intervalMs)to configure the timer period. - Injects pointers to the pipeline’s
GParamManagerandGEventManager, enabling the daemon to publish messages or modify shared state.
Lifecycle Stages
A daemon progresses through four distinct states:
- Construction – You create the daemon class and pass it to
addGDaemon. - Initialization – When
pipeline->init()runs,GDaemonManager::init()iterates all daemons and callsGDaemon::init(), which starts the internalUTimer. - Execution – The timer thread invokes
daemonTaskeveryinterval_milliseconds until the pipeline stops. - Destruction –
pipeline->destroy()triggersGDaemonManager::clear(), which stops timers and deletes daemon instances.
Thread Safety Model
Each daemon operates on its own dedicated timer thread, isolated from the pipeline’s worker thread pool. The timer thread exclusively executes daemonTask and the optional modify hook (which can adjust the next interval dynamically).
Because the pipeline injects GParamManager and GEventManager pointers during registration, your daemon can safely call CGRAPH_PUB_MPARAM or modify parameters using the same thread-safe mechanisms available to standard nodes.
Implementing a Custom GDaemon
Basic Monitor Daemon
The simplest daemon overrides daemonTask to perform periodic logging or health checks. Here is a minimal monitor implementation based on tutorial/MyGDaemon/MyMonitorDaemon.h:
// MyMonitorDaemon.h
#ifndef CGRAPH_MYMONITORDAEMON_H
#define CGRAPH_MYMONITORDAEMON_H
#include "CGraph.h"
class MyMonitorDaemon : public CGraph::GDaemon {
public:
CVoid daemonTask(CGraph::GDaemonParamPtr) override {
// getInterval() returns the configured period in milliseconds
CGraph::CGRAPH_ECHO(
"----> [MyMonitorDaemon] still running, span = %ld ms",
getInterval());
}
};
#endif // CGRAPH_MYMONITORDAEMON_H
Register this daemon with a 4-second interval:
pipeline->addGDaemon<MyMonitorDaemon>(4000);
Daemon with Custom Parameters
For configuration-heavy tasks, subclass GDaemonParam to pass initialization data. This example mirrors tutorial/MyGDaemon/MyParamDaemon.h:
// MyConnParam.h
#ifndef CGRAPH_MYCONNPARAM_H
#define CGRAPH_MYCONNPARAM_H
#include "CGraph.h"
struct MyConnParam : public CGraph::GDaemonParam {
std::string ip_ = "127.0.0.1";
int port_ = 8080;
};
#endif
// MyParamDaemon.h
#ifndef CGRAPH_MYPARAMDAEMON_H
#define CGRAPH_MYPARAMDAEMON_H
#include "CGraph.h"
#include "MyConnParam.h"
class MyParamDaemon : public CGraph::GDaemon {
public:
CVoid daemonTask(CGraph::GDaemonParamPtr param) override {
auto* p = static_cast<MyConnParam*>(param);
CGraph::CGRAPH_ECHO(
"Param daemon: connecting to %s:%d",
p->ip_.c_str(), p->port_);
}
};
#endif
Attach to the pipeline with the custom parameter:
MyConnParam connParam;
connParam.ip_ = "192.168.1.100";
connParam.port_ = 6666;
pipeline->addGDaemon<MyParamDaemon, MyConnParam>(3500, &connParam);
Real-World Example: Camera Simulation Daemon
In production pipelines, daemons often simulate hardware inputs. The CameraGDaemon from example/E01-AutoPilot.cpp demonstrates publishing messages to the pipeline’s event bus:
#include "CGraph.h"
class CameraGDaemon : public CGraph::GDaemon {
public:
CVoid daemonTask(CGraph::GDaemonParamPtr) override {
auto image = std::make_shared<ImageMParam>();
image->frame_id_ = cur_index_;
std::string info = "this is " + std::to_string(cur_index_) + " image";
memcpy(image->image_buf_, info.c_str(), info.length());
cur_index_++;
// Publish to the pipeline's message topic
CGRAPH_PUB_MPARAM(ImageMParam, EXAMPLE_IMAGE_TOPIC,
image, CGraph::GMessagePushStrategy::WAIT);
}
private:
int cur_index_ = 0;
};
Register with a 1-second interval:
pipeline->addGDaemon<CameraGDaemon>(1000);
Downstream nodes (LaneDetectorGNode, CarDetectorGNode) subscribe to EXAMPLE_IMAGE_TOPIC and process each frame independently of the daemon’s timer thread.
Registering Daemons with GPipeline
The GPipeline class exposes the addGDaemon template method (internally __addGDaemon_4py in src/GraphCtrl/GraphPipeline/GPipeline.cpp) to bind daemons to the pipeline lifecycle.
Syntax:
template<typename TDaemon, typename TParam = GDaemonDefaultParam>
GPipelinePtr addGDaemon(CMSec interval, TParam* param = nullptr);
Parameters:
TDaemon– Your subclass ofGDaemon.TParam– Optional custom parameter type derived fromGDaemonParam.interval– Timer period in milliseconds.param– Pointer to the custom parameter instance (can benullptr).
Complete Example:
void demo_pipeline() {
auto pipeline = CGraph::GPipelineFactory::create();
// Register a processing node
CGraph::GElementPtr node = nullptr;
pipeline->registerGElement<MyNode>(&node, {}, "processor");
// Attach two daemons
pipeline->addGDaemon<MyMonitorDaemon>(4000)
->addGDaemon<MyParamDaemon, MyConnParam>(3500, &connParam);
// Execute 20 pipeline iterations
pipeline->process(20);
CGraph::GPipelineFactory::remove(pipeline);
}
Summary
- GDaemon is an abstract base class in
src/GraphCtrl/GraphDaemon/GDaemon.hdesigned for periodic background tasks that run independently of the main pipeline execution flow. - Implement background work by overriding the
daemonTaskmethod and registering the daemon viaGPipeline::addGDaemon<T>(intervalMs, param). - Daemons are managed by GDaemonManager, which handles initialization and cleanup, while UTimer provides the underlying thread-per-daemon scheduling.
- Each daemon receives access to the pipeline’s GParamManager and GEventManager, enabling safe interaction with shared state and message publishing.
- Use custom GDaemonParam subclasses to inject configuration data into the daemon’s execution context.
Frequently Asked Questions
What is the difference between GDaemon and a regular GNode?
A GNode executes within the pipeline’s dataflow graph, triggered by dependencies and scheduled on the pipeline’s thread pool. A GDaemon runs on its own independent timer thread, executing periodically regardless of the pipeline’s processing state. Daemons are ideal for monitoring, heartbeat signals, or simulating external hardware inputs, while nodes perform the actual data transformation.
How do I pass configuration data to a GDaemon?
Create a struct that inherits from CGraph::GDaemonParam, populate it with your configuration values, and pass a pointer to it as the second argument to addGDaemon. Inside daemonTask, cast the GDaemonParamPtr back to your concrete type. See the MyConnParam example in tutorial/MyGDaemon/MyParamDaemon.h for a working implementation.
Is GDaemon thread-safe for accessing pipeline parameters?
Yes. When you register a daemon via addGDaemon, the pipeline automatically injects pointers to its GParamManager and GEventManager into the daemon instance. The daemon can safely read or modify shared parameters and publish messages using the same thread-safe mechanisms available to standard nodes, because the underlying managers handle synchronization internally.
How do I stop or modify a running daemon's interval?
The daemon's interval is stored in the interval_ member variable (milliseconds). You can override the modify method in your subclass to dynamically adjust the next interval based on runtime conditions. To stop a daemon permanently, you must destroy the pipeline or manually manage the daemon's lifecycle through GDaemonManager::clear(), as there is no public stop() method exposed for individual daemons.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →