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

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 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:

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 at lines 118-125. The thread sleeps for 500 milliseconds (poll_interval_us = 500000) between checks, minimizing CPU overhead while maintaining responsive detection.

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 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 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

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 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 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.

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 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 are available.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →