# How the Parent Watchdog Detects Agent Process Death in codebase-memory-mcp

> Learn how the parent watchdog detects agent process death. It polls getppid every 500ms, compares it to the initial parent PID, and terminates processes immediately if they differ.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: internals
- Published: 2026-07-12

---

**The parent watchdog detects agent process death by polling `getppid()` every 500 milliseconds and comparing it against the initial parent PID recorded at startup, triggering immediate process termination when the values differ.**

The `codebase-memory-mcp` server implements a robust parent watchdog mechanism to prevent orphaned processes when the launching agent dies unexpectedly. This lightweight thread monitors the process hierarchy using POSIX-compliant system calls to ensure the server exits cleanly when its parent terminates. Understanding this detection mechanism is crucial for developers deploying the MCP server in production environments where process lifecycle management matters.

## Initializing the Parent Watchdog Thread

When the MCP server starts, it captures the agent’s process ID immediately. In [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) at lines 31-32 and 107-110, the server records `initial_ppid` using `getpid()` and passes this value to the watchdog thread via the `parent_watchdog_thread` function argument.

The watchdog thread is created using the abstraction layer defined in [`foundation/compat_thread.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/foundation/compat_thread.h):

```c
pid_t initial_ppid = getpid();          // Record the agent's PID
bool parent_watchdog_started = false;
cbm_thread_t parent_watchdog_tid;

/* Create the watchdog thread */
if (cbm_thread_create(&parent_watchdog_tid,
                      PARENT_WATCHDOG_STACK_SIZE,
                      parent_watchdog_thread,
                      &initial_ppid) == 0) {
    (void)cbm_thread_detach(&parent_watchdog_tid);
    parent_watchdog_started = true;
    cbm_log_info("parent.watchdog.start");
} else {
    cbm_log_warn("parent.watchdog.unavailable",
                 "reason", "thread_create_failed");
}

```

The thread is immediately detached using `cbm_thread_detach()` to allow independent execution while the main server continues initialization.

## The 500ms Polling Mechanism

The watchdog operates a simple but effective polling loop defined in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) at lines 118-125. The thread sleeps for 500 milliseconds (`poll_interval_us = 500000`) between checks, minimizing CPU overhead while maintaining responsive detection.

```c
static void *parent_watchdog_thread(void *arg) {
    pid_t initial_ppid = *(pid_t *)arg;
    const unsigned int poll_interval_us = 500000; /* 500 ms */

    while (!atomic_load(&g_shutdown)) {
        cbm_usleep(poll_interval_us);
        if (initial_ppid > 1 && getppid() != initial_ppid) {
            const char msg[] =
                "level=warn msg=parent.exited reason=ppid_changed\n";
            (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
            _exit(0);                     /* Terminate the MCP server */
        }
    }
    return NULL;
}

```

The loop continues until the global shutdown flag `g_shutdown` is set via `atomic_load()`, allowing the watchdog to respect normal server termination flow.

## Detecting Agent Process Death

The actual detection logic resides in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) at lines 127-133 and implements two critical safeguards:

- **The PPID Guard**: The watchdog only acts when `initial_ppid > 1`. This check prevents false positives when the server starts without a proper parent (such as when launched directly by init with `ppid == 1`).
- **Orphan Detection**: When `getppid()` returns a value different from the saved `initial_ppid`, the process has been re-parented—typically to PID 1 (init) after the original agent terminated.

Upon detection, the watchdog writes a structured warning message to `stderr` and calls `_exit(0)` to terminate the MCP server immediately.

## Graceful Shutdown Integration

The watchdog’s exit path triggers the same cleanup routine used by signal handlers. According to the source code in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) at lines 72-80 and 758-760, the `_exit(0)` call ensures the server releases resources, stops the file-watcher, and closes stdin through the `request_shutdown` mechanism.

This design guarantees that:
1. **No orphaned processes** remain when the agent dies
2. **Resource cleanup** occurs even during abnormal termination
3. **Logging consistency** is maintained via `cbm_log_info` and `cbm_log_warn` helpers from [`foundation/log.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/foundation/log.h)

## Cross-Platform Implementation Details

The parent watchdog is **POSIX-specific** and compiled out on Windows using `#ifndef _WIN32` guards. This platform-specific approach exists because:

- **POSIX systems** lack a portable "parent-death" notification (Linux’s `PR_SET_PDEATHSIG` is not universally available)
- **Windows** provides superior parent-death detection through job objects, making polling unnecessary

On Unix-like systems, the 500ms polling interval provides negligible overhead while ensuring the server detects agent death within a half-second window.

## Summary

- The parent watchdog in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) monitors process death by comparing the current `getppid()` against the original parent PID captured at startup.
- A **500ms polling interval** balances responsiveness with minimal CPU usage, implemented via `cbm_usleep()` in the `parent_watchdog_thread` function.
- The guard condition `initial_ppid > 1` prevents false triggers when the server runs as a system service without a traditional parent process.
- Upon detecting parent death, the watchdog writes to `stderr` and calls `_exit(0)`, triggering the same graceful shutdown path as signal handlers.
- This implementation is **POSIX-only**; Windows builds rely on native job object functionality instead.

## Frequently Asked Questions

### How does the parent watchdog detect agent process death without using signals?

The watchdog uses **polling** rather than signals because POSIX lacks a portable parent-death notification mechanism. While Linux offers `PR_SET_PDEATHSIG`, this is not available across all Unix variants. The implementation in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) checks `getppid()` every 500 milliseconds to determine if the process has been re-parented to PID 1, indicating the original agent has terminated.

### Why does the watchdog check if initial_ppid > 1 before acting?

This guard condition prevents false shutdowns when the MCP server starts without a proper parent process. If the server is launched directly by the init system (where `ppid == 1`), the watchdog skips detection logic because there is no meaningful "parent process" to monitor. This check appears at line 127 in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c).

### What happens when the parent process dies?

When the parent process dies, the operating system re-parents the MCP server to PID 1 (init). The watchdog detects this change when `getppid() != initial_ppid`, writes a warning message to `stderr` via `write(STDERR_FILENO, ...)`, and calls `_exit(0)` to terminate the server immediately according to the logic in [`src/main.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/main.c) lines 129-133.

### Is the parent watchdog available on Windows?

**No**, the parent watchdog is compiled out on Windows using `#ifndef _WIN32` preprocessor directives. According to the codebase-memory-mcp source code, Windows provides parent-death detection through job objects, which is more efficient than polling. The watchdog thread only exists in POSIX builds where `cbm_thread_create` and `cbm_thread_detach` from [`foundation/compat_thread.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/foundation/compat_thread.h) are available.