Include:
#include <sintra/sintra.h>Summary:
set_lifecycle_handler registers a coordinator-side callback that is
invoked once per process lifecycle event (crash, normal exit, or
unpublished without prior signal). The callback receives a
process_lifecycle_event with the affected process's identity and the
reason. A crash event carries the integer supplied by the internal
Managed_process::terminated_abnormally message, whose prefix also carries
the exact reader generation that authorizes the crash retirement transaction.
Signature:
void set_lifecycle_handler(Lifecycle_handler handler);
using Lifecycle_handler =
std::function<void(const process_lifecycle_event&)>;
struct process_lifecycle_event
{
enum class reason
{
crash,
normal_exit,
unpublished
};
instance_id_type process_iid = invalid_instance_id;
uint32_t process_slot = 0;
reason why = reason::unpublished;
int status = 0;
};
// Internal: not for direct use by application code.
struct Managed_process {
SINTRA_MESSAGE_RESERVED(terminated_abnormally, int status);
};Use when:
- The coordinator needs to react to peer departures: log the cause, display a "crashed" indicator versus a "completed" indicator, or trigger a follow-up workflow.
- A test or supervisor wants to wait for a specific peer to leave and inspect the reason. Combine with a condition variable or future to observe the event from outside the callback.
Contract:
- Effective only on the coordinator process. Calls from non-coordinator processes are no-ops.
- The handler is invoked once for each successful process-publication retirement. Crash status is passed directly into that exact transaction; it is not cached by process instance id for a later unpublish. A duplicate or stale crash message therefore cannot produce another event or retire a replacement generation.
process_iidis the affected process's instance id;process_slotis the stable slot index for correlation across recoveries.- For
crash,statusis exactly the integer carried byManaged_process::terminated_abnormally. The signal-dispatch path supplies the signal number. This field is not a normalized exit code or native wait status and is not guaranteed to be non-zero. It is zero onnormal_exitandunpublishedpaths. For OS-authoritative normalized and native managed-child status, useManaged_child_custody::observe_latest_created_exit(). - The reasons map to the lifecycle as follows:
crash: an exact abnormal-termination message retired its matching managed-child or external-reader generation with that message's status.normal_exit: no crash information was supplied and the process slot was already draining when exact unpublish committed. This usually follows the process beginningshutdown(),leave(), ordetail::finalize(), but coordinator shutdown also marks admitted external processes as draining. The reason therefore does not prove that the peer called a graceful API or that its OS process exited cleanly.unpublished: no crash information was supplied and the process slot was not previously draining when exact unpublish committed.
Managed_process::terminated_abnormallyis an internal reserved message emitted by the abnormal process path. Its message prefix carries managed custody identity and occurrence, or the external invitation occurrence. The coordinator revalidates that identity against the registry and reader before committing unpublish. It is not part of the user message surface; use theLifecycle_handlercallback for crash observation.
Threading and lifecycle:
- Publication registry removal is committed atomically before the handler.
instance_unpublishedand any released delayed publications are enqueued on the coordinator request-ring FIFO before a replacement publication can be enqueued. The handler then runs outside coordinator publication, group, custody, and reader locks. Managed-child communication retirement has been requested, but its asynchronous reader join can still be incomplete; the handler does not certify communication quiescence or custody release. Recovery, when eligible, is considered only after the handler returns. - The handler runs on a coordinator thread. Keep work bounded and thread-safe; different events may invoke it from different threads.
- The handler is set globally on the coordinator. Replacing it with a
new value supersedes the previous one; passing an empty
std::functionclears it. - On Windows, Sintra does not install or own the process unhandled-exception
filter. A host that owns the final unhandled disposition may call
announce_fatal_windows_exceptiononly after deciding that execution will not continue. The existing per-thread CRT signal path is separate and unchanged. - Crash notification runs inside the failing process and is necessarily best-effort if corruption or termination prevents its bounded dispatch from completing. Managed-child exit observation remains OS-authoritative for the native exit status, but a Windows exit code alone is not crash provenance.
Failures:
- None. Exceptions thrown by the handler are caught by the runtime's exception boundary and logged.
Example source:
See also: