# How to Use GDaemon for Background Tasks in CGraph: A Complete Guide

> Master background tasks in CGraph using GDaemon. Inherit the GDaemon class, override daemonTask, and register with GPipeline for efficient background processing. Get the complete guide.

- Repository: [Chunel/cgraph](https://github.com/chunelfeng/cgraph)
- Tags: how-to-guide
- Published: 2026-02-27

---

**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`](https://github.com/chunelfeng/cgraph/blob/main/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 virtual `daemonTask` method.
- **`GDaemonObject`** – Defined in [`src/GraphCtrl/GraphDaemon/GDaemonObject.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphDaemon/GDaemonObject.h), provides common utilities including parameter managers and interval setters.
- **`GDaemonManager`** – Implemented in [`src/GraphCtrl/GraphDaemon/GDaemonManager.cpp`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphDaemon/GDaemonManager.cpp), owns daemon instances and manages their `init()` and `destroy()` 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`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphPipeline/GPipeline.cpp)) performs three critical actions:

1. Instantiates your daemon template.
2. Invokes `setInterval(intervalMs)` to configure the timer period.
3. Injects pointers to the pipeline’s `GParamManager` and `GEventManager`, enabling the daemon to publish messages or modify shared state.

### Lifecycle Stages

A daemon progresses through four distinct states:

1. **Construction** – You create the daemon class and pass it to `addGDaemon`.
2. **Initialization** – When `pipeline->init()` runs, `GDaemonManager::init()` iterates all daemons and calls `GDaemon::init()`, which starts the internal `UTimer`.
3. **Execution** – The timer thread invokes `daemonTask` every `interval_` milliseconds until the pipeline stops.
4. **Destruction** – `pipeline->destroy()` triggers `GDaemonManager::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`](https://github.com/chunelfeng/cgraph/blob/main/tutorial/MyGDaemon/MyMonitorDaemon.h):

```cpp
// 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:

```cpp
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`](https://github.com/chunelfeng/cgraph/blob/main/tutorial/MyGDaemon/MyParamDaemon.h):

```cpp
// 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

```

```cpp
// 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:

```cpp
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`](https://github.com/chunelfeng/cgraph/blob/main/example/E01-AutoPilot.cpp) demonstrates publishing messages to the pipeline’s event bus:

```cpp
#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:

```cpp
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`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphPipeline/GPipeline.cpp)) to bind daemons to the pipeline lifecycle.

**Syntax:**

```cpp
template<typename TDaemon, typename TParam = GDaemonDefaultParam>
GPipelinePtr addGDaemon(CMSec interval, TParam* param = nullptr);

```

**Parameters:**

- **`TDaemon`** – Your subclass of `GDaemon`.
- **`TParam`** – Optional custom parameter type derived from `GDaemonParam`.
- **`interval`** – Timer period in milliseconds.
- **`param`** – Pointer to the custom parameter instance (can be `nullptr`).

**Complete Example:**

```cpp
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.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphDaemon/GDaemon.h) designed for periodic background tasks that run independently of the main pipeline execution flow.
- Implement background work by overriding the `daemonTask` method and registering the daemon via `GPipeline::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`](https://github.com/chunelfeng/cgraph/blob/main/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.