diff --git a/cuwrapper/rc/rc_server_abi.cpp b/cuwrapper/rc/rc_server_abi.cpp index 7d7c5e6a..948cce72 100644 --- a/cuwrapper/rc/rc_server_abi.cpp +++ b/cuwrapper/rc/rc_server_abi.cpp @@ -101,6 +101,11 @@ struct rc_server { * races an in-flight sink pointer swap. */ rc_log_fn log_fn = nullptr; void *log_ctx = nullptr; + /* Terminal notification sink: same lifetime contract as log_fn + * (installed once at init, cleared by destroy after the reaper + * joined). Fired by reapSession with no lock held. */ + rc_terminal_fn term_fn = nullptr; + void *term_ctx = nullptr; std::atomic epoch_counter{1}; /* resource accounting (global buckets; per-principal map). */ std::mutex acct_mtx; @@ -253,6 +258,13 @@ void reapSession(rc_server *srv, RcSession *s) { * is actually gone: a surviving QP still holds the device. */ if (destroyed) hipObj::v2::releaseDevice(srv->device); limitsRelease(srv, s->principal, s->staging_len); + /* Terminal notification: the sink runs after every server-side + * bookkeeping above so it observes the session as fully gone, + * and no lock is held here per the callback contract. */ + rc_terminal_fn tfn = srv->term_fn; + if (tfn) + tfn(srv->term_ctx, s->core.id.c_str(), s->last_outcome, + (uint64_t)s->staging_len); } /* Runs the reap pass: sessions marked reap_pending (or in the @@ -301,6 +313,13 @@ void rc_server_set_log_sink(rc_server *srv, rc_log_fn fn, void *ctx) { srv->log_ctx = ctx; } +void rc_server_set_terminal_notify(rc_server *srv, rc_terminal_fn fn, + void *ctx) { + if (!srv) return; + srv->term_fn = fn; + srv->term_ctx = ctx; +} + int rc_server_init(const rc_device_opts *opts, rc_server **out) { if (!opts || !out) return RC_E_ARG; if (!hipObj::ibv.ensureLoaded()) { diff --git a/cuwrapper/rc/rc_server_abi.h b/cuwrapper/rc/rc_server_abi.h index f852c19b..9193524a 100644 --- a/cuwrapper/rc/rc_server_abi.h +++ b/cuwrapper/rc/rc_server_abi.h @@ -52,6 +52,21 @@ typedef struct rc_server rc_server; void rc_server_set_log_sink(rc_server *srv, rc_log_fn fn, void *ctx); +/* Terminal session notification. Fired exactly once per session + * when the reaper destroys it (expiry, CANCEL, or destroy), + * carrying the last outcome observed for the session. `id` is + * only valid for the duration of the call. Called with no server + * lock held; the sink must return promptly and must not call back + * into the server. Sessions that end inside a READY/FinishPut + * handler still fire this notification after the handler's own + * terminal bookkeeping, so the sink can treat it as the single + * authoritative "the session is gone" signal. */ +typedef void (*rc_terminal_fn)(void *ctx, const char *id, int outcome, + uint64_t bytes); + +void rc_server_set_terminal_notify(rc_server *srv, rc_terminal_fn fn, + void *ctx); + /* Device selection: matching GID prefix when gid_hint is set, * otherwise the first verbs device. */ typedef struct {