ワンクリックで
subsystem-summary-of-process
read this skill for a token-efficient summary of the process subsystem
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
read this skill for a token-efficient summary of the process subsystem
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
extending yourself with a new reusable skill by interviewing the user
analyzing a change to determine what tests are needed and adding them to the test suite
modifying build configuration to enable/disable variants, switch compilers or flags, or otherwise prepare for a build
reviewing a change for semantic correctness, simplicity, design consistency, and completeness
reviewing a git diff for small localized coding mistakes that can be fixed without high-level understanding
how to run make correctly to get a good build, and otherwise understand the build system
| name | subsystem-summary-of-process |
| description | read this skill for a token-efficient summary of the process subsystem |
The process subsystem provides asynchronous subprocess management for stellar-core, wrapping platform-specific process spawning (POSIX posix_spawnp and Windows CreateProcess) behind a unified asio-integrated interface. It enables running external commands (e.g., history archival tools like gzip, gunzip, curl) asynchronously, with configurable concurrency limits, output file capture, graceful shutdown, and process lifecycle tracking. No facilities exist for reading/writing subprocess I/O ports — this is strictly for "run a command, wait to see if it worked."
ProcessManager and the ProcessExitEvent class for async process completion notification.ProcessManager; contains all lifecycle management, platform-specific spawning, signal handling, and shutdown logic.posix_spawn_file_actions_t for redirecting subprocess stdout to a file.ProcessManager (abstract, inherits std::enable_shared_from_this<ProcessManager>, NonMovableOrCopyable)The public interface for subprocess management. One ProcessManager exists per Application instance, created via the static factory ProcessManager::create(Application&).
Key virtual methods:
runProcess(cmdLine, outputFile) → std::weak_ptr<ProcessExitEvent> — Queues or immediately launches a subprocess. If outputFile is non-empty, stdout is captured to a temp file and atomically renamed on success.getNumRunningProcesses() — Count of active (non-shutting-down) child processes.getNumRunningOrShuttingDownProcesses() — Count of all tracked child processes including those being terminated.tryProcessShutdown(pe) — Synchronously cancels a ProcessExitEvent and attempts to terminate the associated process (SIGTERM on POSIX, GenerateConsoleCtrlEvent on Windows). Returns true if the termination signal was sent successfully.shutdown() — Marks the manager as shut down, cancels all pending processes, and attempts polite shutdown of all running processes.isShutdown() — Returns whether shutdown has been initiated.ProcessExitEventAn asio-compatible event object that clients use to await subprocess completion. It simulates an event notifier using a RealTimer set to maximum duration. When the subprocess exits (or is cancelled), the timer is cancelled with an appropriate error code.
Members:
mTimer (std::shared_ptr<RealTimer>) — The underlying asio timer used for async waiting.mImpl (std::shared_ptr<Impl>) — Platform-specific implementation details (command line, output file, process handle, lifecycle state).mEc (std::shared_ptr<asio::error_code>) — Shared error code for communicating the real exit status past asio's timer cancellation (which always delivers operation_aborted).Key methods:
async_wait(handler) — Registers a callback that fires when the process exits. The handler receives an asio::error_code where a zero value means success, and a non-zero value encodes the process exit status.ProcessExitEvent::Impl (internal, inherits std::enable_shared_from_this)Holds all per-process state and manages the platform-specific spawning logic.
Members:
mOuterTimer / mOuterEc — Shared pointers back to the owning ProcessExitEvent's timer and error code.mCmdLine (std::string const) — The command line to execute.mOutFile (std::string const) — The desired final output file path (may be empty if no capture).mTempFile (std::string const) — Temporary file path for capturing stdout before atomic rename.mLifecycle (ProcessLifecycle) — Tracks the process through its states.mProcessId (int) — PID of the spawned process (-1 before launch).mProcessHandle (Windows only, asio::windows::object_handle) — Waitable handle for the Windows process object.mProcManagerImpl (std::weak_ptr<ProcessManagerImpl>) — Weak back-reference to the owning manager.Key methods:
run() — Platform-specific process spawning. On POSIX: splits the command line, sets up PosixSpawnFileActions for output redirection, sets FD_CLOEXEC on all open file descriptors ≥ 3, then calls posix_spawnp(). On Windows: sets up STARTUPINFOEX with inheritable handles, calls CreateProcess(), and registers an async wait on the process handle.finish() — Called on termination; renames the temp file to the final output file (atomic move). Returns false if the rename fails.cancel(ec) — Sets the outer error code and cancels the outer timer, firing all registered async_wait handlers.ProcessManagerImpl (inherits ProcessManager)The concrete implementation that manages the full lifecycle of subprocesses.
Members:
mProcessesMutex (std::recursive_mutex) — Guards mProcesses and mPending since subprocess exits arrive asynchronously.mProcesses (std::map<int, std::shared_ptr<ProcessExitEvent>>) — Maps PID to running or shutting-down processes.mPending (std::deque<std::shared_ptr<ProcessExitEvent>>) — Queue of processes waiting to be launched (when concurrency limit is reached).mIsShutdown (bool) — Set once shutdown() is called.mMaxProcesses (size_t const) — Maximum concurrent subprocesses, from Config::MAX_CONCURRENT_SUBPROCESSES.mIOContext (asio::io_context&) — The application's I/O context for async operations.mSigChild (asio::signal_set) — On POSIX, listens for SIGCHLD to detect child process exits. Unused on Windows.mTmpDir (std::unique_ptr<TmpDir>) — Temporary directory for capturing subprocess output files.mTempFileCount (uint64_t) — Monotonic counter for generating unique temp file names.ProcessLifecycle (enum, anonymous namespace)Tracks the state of a subprocess through its lifetime:
PENDING (0) — Queued, not yet spawned.RUNNING (1) — Spawned, waiting for exit.TRIED_POLITE_SHUTDOWN (2) — SIGTERM (POSIX) or CTRL_C_EVENT (Windows) sent.TRIED_FORCED_SHUTDOWN (3) — SIGKILL (POSIX) or TerminateProcess (Windows) sent.TERMINATED (5) — Exit detected and handled.PosixSpawnFileActions (POSIX only)RAII wrapper around posix_spawn_file_actions_t. Lazily initializes the actions object on first addOpen() call. Provides an implicit conversion to posix_spawn_file_actions_t* (returns nullptr if never initialized, meaning no file actions).
Key methods:
addOpen(fildes, fileName, oflag, mode) — Registers a file-open action for the child process (used to redirect fd 1 / stdout to a temp file).initialize() — Calls posix_spawn_file_actions_init(); idempotent.posix_spawn_file_actions_destroy() if initialized.ProcessManagerImpl::runProcess(cmdLine, outFile).ProcessExitEvent is created with a RealTimer set to max duration.ProcessExitEvent::Impl is created holding the command line, output file, a generated temp file path, and a weak reference to the manager.mPending.maybeRunPendingProcesses() is called, which pops events from mPending while getNumRunningOrShuttingDownProcesses() < mMaxProcesses.Impl::run() is called:
PosixSpawnFileActions is set up if output capture is needed. All file descriptors ≥ 3 are marked FD_CLOEXEC. posix_spawnp() is called. Lifecycle transitions to RUNNING.STARTUPINFOEX and handle inheritance are configured. CreateProcess() is called with CREATE_NEW_PROCESS_GROUP. An async wait is registered on the process handle. Lifecycle transitions to RUNNING.mProcesses.weak_ptr<ProcessExitEvent> is returned to the caller, who calls async_wait() to register a completion handler.SIGCHLD arrives, handled by asio::signal_set → handleSignalChild().handleSignalChild() re-registers the signal handler (via startWaitingForSignalChild()), then calls reapChildren().reapChildren() iterates all tracked PIDs, calling waitpid(pid, &status, WNOHANG) for each.handleProcessTermination(pid, status) is called.handleProcessTermination() maps the exit status to an asio::error_code (via mapExitStatusToErrorCode), calls Impl::finish() to rename the temp output file, removes the process from mProcesses, calls maybeRunPendingProcesses() to launch queued processes, then fires the callback via Impl::cancel(ec).asio::windows::object_handle::async_wait fires when the process handle becomes signaled.GetExitCodeProcess() and passes the result to handleProcessTermination().shutdown() sets mIsShutdown = true.ABORT_ERROR_CODE.tryProcessShutdownAll() iterates all running processes and calls tryProcessShutdown() on each.tryProcessShutdown() uses a two-phase approach based on lifecycle state:
RUNNING: calls politeShutdown() (SIGTERM / CTRL_C_EVENT), advances to TRIED_POLITE_SHUTDOWN.TRIED_POLITE_SHUTDOWN: calls forcedShutdown() (SIGKILL / TerminateProcess), advances to TRIED_FORCED_SHUTDOWN.~ProcessManagerImpl()) ensures cleanup: it cancels the SIGCHLD handler, calls shutdown(), then loops up to 3 times sleeping 10ms, reaping children, and re-triggering progressively more forceful shutdown.mMaxProcesses (from Config::MAX_CONCURRENT_SUBPROCESSES).mPending (a FIFO deque).handleProcessTermination), maybeRunPendingProcesses() is called to launch queued processes up to the limit.mProcesses and mPending is guarded by mProcessesMutex (a recursive mutex), since process exits arrive asynchronously from signal handlers or async I/O callbacks.Application
└── ProcessManagerImpl (shared_ptr, via ProcessManager::create)
├── mProcesses: map<pid, shared_ptr<ProcessExitEvent>>
│ └── ProcessExitEvent
│ ├── mTimer: shared_ptr<RealTimer>
│ ├── mEc: shared_ptr<asio::error_code>
│ └── mImpl: shared_ptr<ProcessExitEvent::Impl>
│ └── mProcManagerImpl: weak_ptr<ProcessManagerImpl> (back-ref)
├── mPending: deque<shared_ptr<ProcessExitEvent>>
├── mSigChild: asio::signal_set (POSIX only)
└── mTmpDir: unique_ptr<TmpDir>
ProcessManagerImpl owns all ProcessExitEvent objects (via mProcesses and mPending).ProcessExitEvent::Impl holds a weak_ptr back to ProcessManagerImpl to avoid circular ownership.weak_ptr<ProcessExitEvent> from runProcess(), so the manager controls the event's lifetime.Impl::run() method on Windows captures a shared_from_this() to keep Impl alive through the async wait callback.runProcess(cmdLine, outFile) → queued in mPending → dequeued by maybeRunPendingProcesses() → Impl::run() spawns the OS process.reapChildren() / handle callback → handleProcessTermination() → Impl::finish() (rename temp file) → Impl::cancel(ec) → timer cancelled → async_wait handler fires with exit code.mTmpDir. On successful termination, Impl::finish() atomically renames the temp file to the requested output file path. If the output file already exists, the rename fails and an error is propagated.mapExitStatusToErrorCode() translates OS-level exit status (including POSIX WIFEXITED/WEXITSTATUS macros) into asio::error_code. Special handling for exit code 127 on Linux (likely missing command). On Windows, the exit code is used directly.shutdown() → cancel pending → polite shutdown (SIGTERM) → forced shutdown (SIGKILL) → destructor reaps remaining children with retry loop.| Aspect | POSIX | Windows |
|---|---|---|
| Process spawning | posix_spawnp() | CreateProcess() with EXTENDED_STARTUPINFO_PRESENT |
| Exit detection | SIGCHLD via asio::signal_set + waitpid(WNOHANG) | asio::windows::object_handle::async_wait |
| Output redirection | posix_spawn_file_actions_addopen() on fd 1 | CreateFile() + STARTF_USESTDHANDLES |
| Polite shutdown | kill(pid, SIGTERM) | GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid) |
| Forced shutdown | kill(pid, SIGKILL) | TerminateProcess() |
| FD cleanup | FD_CLOEXEC on fds 3..SC_OPEN_MAX (with gap heuristic) | Handle inheritance via PROC_THREAD_ATTRIBUTE_HANDLE_LIST |
posix_spawnp() or CreateProcess() fails, the ProcessExitEvent is cancelled with std::errc::io_error and the error is logged.posix_spawnp does not fault on file-not-found in the parent).async_wait pattern works around asio always delivering operation_aborted on timer cancel: the real error code is stored in a shared mEc variable and the handler reads from that instead of using the asio-provided code.checkInvariants() validates consistency: pending processes must be in PENDING state, running processes must not be PENDING, and PIDs must match map keys. Called at key state transitions.