const std = @import("std");const builtin = @import("builtin");const io_mod = @import("../shared/io.zig");const debug_trace = @import("../shared/debug_trace.zig");const background_launch_identity = @import("background_launch_identity.zig");const background_launch_output = @import("background_launch_output.zig");const background_record_liveness = @import("background_record_liveness.zig");const background_record_restore = @import("background_record_restore.zig");const background_store = @import("background_store.zig");const process_supervisor = @import("process_supervisor.zig");const server_detection = @import("server_detection.zig");const command_contract = @import("../execution/command_contract.zig");const background_process_provider = @import( "../execution/background_process_provider.zig",);const session_child_store = @import("../session/session_child_store.zig");const session = @import("../session/session.zig");const session_codec = @import("../session/session_codec.zig");const types = @import("../shared/types.zig");const session_store = @import("../session/session_store.zig");const task_helpers = @import("../tasks/task_helpers.zig"); const Allocator = std.mem.Allocator; test { _ = background_launch_identity; _ = background_launch_output; _ = background_record_liveness; _ = background_record_restore;} pub const RuntimeContextSnapshot = process_supervisor.RuntimeContextSnapshot;pub const TaskSnapshot = process_supervisor.TaskSnapshot;pub const TaskListSnapshot = process_supervisor.TaskListSnapshot;pub const TaskState = process_supervisor.TaskState;pub const TaskSelection = process_supervisor.TaskSelection;pub const StopSelection = process_supervisor.StopSelection;pub const TaskCompletion = process_supervisor.TaskCompletion;pub const BackgroundLaunchPolicy = process_supervisor.BackgroundLaunchPolicy;pub const StableBackgroundRecordId = process_supervisor.StableBackgroundRecordId; pub const PreparedBackgroundLaunch = struct { identity: background_launch_identity.Identity, output: background_launch_output.Output, consumed: bool = false,}; pub const BackgroundLaunchOutcome = enum { process_local_started, durable_started, durable_started_degraded,}; pub const RegisteredBackground = struct { process_id: u64, outcome: BackgroundLaunchOutcome, command: command_contract.BackgroundCommand, pub fn deinit(self: *RegisteredBackground, alloc: Allocator) void { alloc.free(@constCast(self.command.pid)); alloc.free(@constCast(self.command.command)); alloc.free(@constCast(self.command.cwd)); alloc.free(@constCast(self.command.log_path)); if (self.command.url) |url| alloc.free(@constCast(url)); self.* = undefined; }}; pub const UrlReadyCallback = *const fn (ctx: *anyopaque, task_id: u64, url: []const u8) void;pub const WatcherContextDeinit = *const fn (alloc: Allocator, ctx: *anyopaque) void;pub const TaskCompletionCallback = *const fn (ctx: *anyopaque, completion: TaskCompletion) void; const BackgroundUrlWatchJob = struct { alloc: Allocator, runtime: *BackgroundRuntime, process_id: u64, callback_ctx: *anyopaque, on_url_ready: UrlReadyCallback, on_context_deinit: ?WatcherContextDeinit, done: *std.atomic.Value(bool),}; const BackgroundWatcherHandle = struct { thread: std.Thread, done: *std.atomic.Value(bool),}; const RetainedSourceCapability = struct { source_session_id: []u8, capability: *session_child_store.SessionChildCapability, fn deinit(self: *RetainedSourceCapability, alloc: Allocator) void { self.capability.deinit(); alloc.destroy(self.capability); alloc.free(self.source_session_id); self.* = undefined; }}; const OwnedBackgroundChild = struct { process_id: u64, process: background_process_provider.OwnedProcess,}; const watcher_retry_interval_ns = 200 * std.time.ns_per_ms;const process_identity_capture_attempts: usize = 3;const process_identity_capture_retry_delay_ns = 20 * std.time.ns_per_ms;const blocked_wrapper_cleanup_timeout_ms: i64 = 2000;var blocked_wrapper_cleanup_timeout_ms_for_test: ?i64 = null; const StableRecordIdFn = *const fn () anyerror!StableBackgroundRecordId;var stable_record_id_for_test: ?StableRecordIdFn = null; pub const BackgroundRuntime = struct { process_provider: background_process_provider.Provider = background_process_provider.unavailable_provider, mutex: std.Io.Mutex = .init, supervisor: process_supervisor.ProcessSupervisor = .{}, persisted_store: ?background_store.Store = null, owned_session_capability: ?*session_child_store.SessionChildCapability = null, borrowed_session_capability: ?*session_child_store.SessionChildCapability = null, source_session_id: ?[]u8 = null, retained_source_capabilities: std.ArrayList(RetainedSourceCapability) = .empty, retained_indeterminate_identities: std.ArrayList(background_launch_identity.Identity) = .empty, prepared_launch_reservations: usize = 0, owned_children: std.ArrayList(OwnedBackgroundChild) = .empty, watchers: std.ArrayList(BackgroundWatcherHandle) = .empty, stop_requested: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), last_refresh_ms: i64 = 0, pub fn init( process_provider: background_process_provider.Provider, ) BackgroundRuntime { return .{ .process_provider = process_provider }; } pub fn processProvider( self: *const BackgroundRuntime, ) background_process_provider.Provider { return self.process_provider; } pub fn deinit(self: *BackgroundRuntime, alloc: Allocator) void { if (self.source_session_id) |source_session_id| { self.retryDegradedRecordsForSource( alloc, source_session_id, ); } self.requestStop(); self.pruneWatchers(alloc, true); self.terminateOwnedUnrecordedProcesses(alloc); self.watchers.deinit(alloc); for (self.owned_children.items) |*owned| { owned.process.forget(); } self.owned_children.deinit(alloc); if (self.persisted_store) |*store| store.deinit(alloc); for (self.retained_source_capabilities.items) |*retained| { retained.deinit(alloc); } self.retained_source_capabilities.deinit(alloc); for (self.retained_indeterminate_identities.items) |*identity| { identity.deinit(alloc); } self.retained_indeterminate_identities.deinit(alloc); if (self.owned_session_capability) |capability| { capability.deinit(); alloc.destroy(capability); } if (self.source_session_id) |source_session_id| { alloc.free(source_session_id); } self.supervisor.deinit(alloc); self.* = .{}; } pub fn enablePersistence(self: *BackgroundRuntime, alloc: Allocator, background_dir: []const u8) !void { const session_dir_path = std.fs.path.dirname(background_dir) orelse return error.SessionChildStoreFailed; const source_session_id = std.fs.path.basename(session_dir_path); const capability = try alloc.create( session_child_store.SessionChildCapability, ); var capability_owned = true; errdefer if (capability_owned) alloc.destroy(capability); capability.* = try session_child_store.SessionChildCapability.initLegacyBackgroundRoutes( alloc, background_dir, .writable, ); errdefer if (capability_owned) capability.deinit(); self.installPersistence( alloc, capability, source_session_id, true, ) catch |err| { if (self.activeSessionCapability() == capability) { capability_owned = false; } return err; }; capability_owned = false; } pub fn enableManagedPersistence( self: *BackgroundRuntime, alloc: Allocator, capability: *session_child_store.SessionChildCapability, source_session_id: []const u8, ) !void { try self.installPersistence( alloc, capability, source_session_id, false, ); } fn installPersistence( self: *BackgroundRuntime, alloc: Allocator, capability: *session_child_store.SessionChildCapability, source_session_id: []const u8, owns_capability: bool, ) !void { var store = background_store.Store.initManaged(capability); const owned_source_session_id = try alloc.dupe( u8, source_session_id, ); errdefer alloc.free(owned_source_session_id); if (self.source_session_id) |existing_source_session_id| { if (std.mem.eql( u8, existing_source_session_id, owned_source_session_id, ) and self.activeSessionCapability() == capability) { alloc.free(owned_source_session_id); return; } self.invalidateSourceAuthority( alloc, existing_source_session_id, ); } self.mutex.lockUncancelable(io_mod.getIo()); errdefer self.mutex.unlock(io_mod.getIo()); const next_id = try store.nextId(); if (self.persisted_store) |*existing| existing.deinit(alloc); if (self.source_session_id) |closing_source_session_id| { for (self.supervisor.tasks.items) |*task| { const task_source = task.source_session_id orelse continue; if (!std.mem.eql( u8, task_source, closing_source_session_id, )) continue; task.record_authority = .none; } } if (self.owned_session_capability) |existing| { existing.deinit(); alloc.destroy(existing); } self.borrowed_session_capability = null; if (self.source_session_id) |existing| alloc.free(existing); self.persisted_store = store; if (owns_capability) { self.owned_session_capability = capability; } else { self.owned_session_capability = null; self.borrowed_session_capability = capability; } self.source_session_id = owned_source_session_id; self.supervisor.next_background_process_id = @max(self.supervisor.next_background_process_id, next_id); for (self.supervisor.tasks.items) |*task| { const task_source = task.source_session_id orelse continue; const stable_id = task.background_record_id orelse continue; if (!std.mem.eql( u8, task_source, owned_source_session_id, )) continue; if (store.loadByStableId(alloc, stable_id)) |record| { var current = record; defer current.deinit(alloc); const token = current.process_token orelse continue; const expected = process_supervisor.ProcessInstanceToken.parse( token, ) catch continue; const token_match = self.process_provider.matchToken( alloc, current.pid, expected, ); switch (token_match) { .matched => { task.record_authority = .{ .writable = capability, }; }, .missing, .mismatched => { task.record_authority = .{ .writable = capability, }; if (task.state == .running) { task.state = .stale; task.expect_url = false; task.exit_code = null; } }, .unavailable => {}, } } else |_| {} } const snapshots = self.supervisor.snapshotTasks(alloc) catch |err| { if (self.persisted_store) |*existing| existing.deinit(alloc); self.persisted_store = null; return err; }; self.mutex.unlock(io_mod.getIo()); defer snapshots.deinit(alloc); self.saveSnapshotsOrDisablePersistence(alloc, "enable background persistence", snapshots.items); } pub fn invalidateSourceAuthority( self: *BackgroundRuntime, alloc: Allocator, source_session_id: []const u8, ) void { self.retryDegradedRecordsForSource( alloc, source_session_id, ); self.mutex.lockUncancelable(io_mod.getIo()); defer self.mutex.unlock(io_mod.getIo()); for (self.supervisor.tasks.items) |*task| { const task_source = task.source_session_id orelse continue; if (!std.mem.eql( u8, task_source, source_session_id, )) continue; task.record_authority = .none; } } pub fn detachManagedPersistence( self: *BackgroundRuntime, alloc: Allocator, source_session_id: []const u8, ) void { const current = self.source_session_id orelse return; if (!std.mem.eql(u8, current, source_session_id)) return; self.invalidateSourceAuthority(alloc, source_session_id); self.mutex.lockUncancelable(io_mod.getIo()); defer self.mutex.unlock(io_mod.getIo()); if (self.persisted_store) |*store| store.deinit(alloc); self.persisted_store = null; if (self.owned_session_capability) |capability| { capability.deinit(); alloc.destroy(capability); } self.owned_session_capability = null; self.borrowed_session_capability = null; if (self.source_session_id) |owned| alloc.free(owned); self.source_session_id = null; } fn activeSessionCapability( self: *BackgroundRuntime, ) ?*session_child_store.SessionChildCapability { return self.borrowed_session_capability orelse self.owned_session_capability; } fn retryDegradedRecordsForSource( self: *BackgroundRuntime, alloc: Allocator, source_session_id: []const u8, ) void { self.mutex.lockUncancelable(io_mod.getIo()); const snapshots = self.supervisor.snapshotTasks(alloc) catch { self.mutex.unlock(io_mod.getIo()); return; }; self.mutex.unlock(io_mod.getIo()); defer snapshots.deinit(alloc); for (snapshots.items) |task_snapshot| { const task_source = task_snapshot.source_session_id orelse continue; if (!std.mem.eql( u8, task_source, source_session_id, )) continue; if (task_snapshot.record_persistence != .initial_record_degraded and task_snapshot.record_persistence != .record_update_degraded) { continue; } self.saveDurableSnapshotIfAuthorized( alloc, "final source authority retry", task_snapshot, ); } } pub fn prepareBackgroundLaunch( self: *BackgroundRuntime, alloc: Allocator, policy: BackgroundLaunchPolicy, ) !PreparedBackgroundLaunch { self.mutex.lockUncancelable(io_mod.getIo()); var locked = true; errdefer if (locked) self.mutex.unlock(io_mod.getIo()); const reserved_retention_slots = std.math.add( usize, self.prepared_launch_reservations, 1, ) catch return error.BackgroundIdentityUnavailable; try self.owned_children.ensureUnusedCapacity( alloc, reserved_retention_slots, ); try self.retained_indeterminate_identities.ensureUnusedCapacity( alloc, reserved_retention_slots, ); const display_id = try self.supervisor.reserveDisplayId(); errdefer self.supervisor.releaseDisplayId(display_id); var identity: background_launch_identity.Identity = undefined; switch (policy) { .process_local_long_lived => { identity = .{ .process_local_long_lived = .{ .display_id = display_id, } }; }, .durable_long_lived, .saved_headless => { const source_session_id = self.source_session_id orelse return error.BackgroundPersistenceUnavailable; const store = self.persisted_store orelse return error.BackgroundPersistenceUnavailable; const stable_id = try self.generateStableRecordIdLocked( alloc, store, source_session_id, ); const owned_source_session_id = try alloc.dupe( u8, source_session_id, ); identity = switch (policy) { .durable_long_lived => .{ .durable_long_lived = .{ .display_id = display_id, .source_session_id = owned_source_session_id, .background_record_id = stable_id, }, }, .saved_headless => .{ .saved_headless = .{ .display_id = display_id, .source_session_id = owned_source_session_id, .background_record_id = stable_id, }, }, .process_local_long_lived => unreachable, }; }, } self.prepared_launch_reservations += 1; self.mutex.unlock(io_mod.getIo()); locked = false; errdefer self.releasePreparedLaunchReservation(); errdefer identity.deinit(alloc); const output = switch (policy) { .process_local_long_lived => try background_launch_output.prepareExternal(alloc), .durable_long_lived, .saved_headless => blk: { const capability = self.activeSessionCapability() orelse return error.BackgroundPersistenceUnavailable;Lines 1–500 of 4251. Load more
const std = @import("std"); const builtin = @import("builtin"); const io_mod = @import("../shared/io.zig"); const debug_trace = @import("../shared/debug_trace.zig"); const background_launch_identity = @import("background_launch_identity.zig"); const background_launch_output = @import("background_launch_output.zig"); const background_record_liveness = @import("background_record_liveness.zig"); const background_record_restore = @import("background_record_restore.zig"); const background_store = @import("background_store.zig"); const process_supervisor = @import("process_supervisor.zig"); const server_detection = @import("server_detection.zig"); const command_contract = @import("../execution/command_contract.zig"); const background_process_provider = @import( "../execution/background_process_provider.zig", ); const session_child_store = @import("../session/session_child_store.zig"); const session = @import("../session/session.zig"); const session_codec = @import("../session/session_codec.zig"); const types = @import("../shared/types.zig"); const session_store = @import("../session/session_store.zig"); const task_helpers = @import("../tasks/task_helpers.zig"); const Allocator = std.mem.Allocator; test { _ = background_launch_identity; _ = background_launch_output; _ = background_record_liveness; _ = background_record_restore; } pub const RuntimeContextSnapshot = process_supervisor.RuntimeContextSnapshot; pub const TaskSnapshot = process_supervisor.TaskSnapshot; pub const TaskListSnapshot = process_supervisor.TaskListSnapshot; pub const TaskState = process_supervisor.TaskState; pub const TaskSelection = process_supervisor.TaskSelection; pub const StopSelection = process_supervisor.StopSelection; pub const TaskCompletion = process_supervisor.TaskCompletion; pub const BackgroundLaunchPolicy = process_supervisor.BackgroundLaunchPolicy; pub const StableBackgroundRecordId = process_supervisor.StableBackgroundRecordId; pub const PreparedBackgroundLaunch = struct { identity: background_launch_identity.Identity, output: background_launch_output.Output, consumed: bool = false, }; pub const BackgroundLaunchOutcome = enum { process_local_started, durable_started, durable_started_degraded, }; pub const RegisteredBackground = struct { process_id: u64, outcome: BackgroundLaunchOutcome, command: command_contract.BackgroundCommand, pub fn deinit(self: *RegisteredBackground, alloc: Allocator) void { alloc.free(@constCast(self.command.pid)); alloc.free(@constCast(self.command.command)); alloc.free(@constCast(self.command.cwd)); alloc.free(@constCast(self.command.log_path)); if (self.command.url) |url| alloc.free(@constCast(url)); self.* = undefined; } }; pub const UrlReadyCallback = *const fn (ctx: *anyopaque, task_id: u64, url: []const u8) void; pub const WatcherContextDeinit = *const fn (alloc: Allocator, ctx: *anyopaque) void; pub const TaskCompletionCallback = *const fn (ctx: *anyopaque, completion: TaskCompletion) void; const BackgroundUrlWatchJob = struct { alloc: Allocator, runtime: *BackgroundRuntime, process_id: u64, callback_ctx: *anyopaque, on_url_ready: UrlReadyCallback, on_context_deinit: ?WatcherContextDeinit, done: *std.atomic.Value(bool), }; const BackgroundWatcherHandle = struct { thread: std.Thread, done: *std.atomic.Value(bool), }; const RetainedSourceCapability = struct { source_session_id: []u8, capability: *session_child_store.SessionChildCapability, fn deinit(self: *RetainedSourceCapability, alloc: Allocator) void { self.capability.deinit(); alloc.destroy(self.capability); alloc.free(self.source_session_id); self.* = undefined; } }; const OwnedBackgroundChild = struct { process_id: u64, process: background_process_provider.OwnedProcess, }; const watcher_retry_interval_ns = 200 * std.time.ns_per_ms; const process_identity_capture_attempts: usize = 3; const process_identity_capture_retry_delay_ns = 20 * std.time.ns_per_ms; const blocked_wrapper_cleanup_timeout_ms: i64 = 2000; var blocked_wrapper_cleanup_timeout_ms_for_test: ?i64 = null; const StableRecordIdFn = *const fn () anyerror!StableBackgroundRecordId; var stable_record_id_for_test: ?StableRecordIdFn = null; pub const BackgroundRuntime = struct { process_provider: background_process_provider.Provider = background_process_provider.unavailable_provider, mutex: std.Io.Mutex = .init, supervisor: process_supervisor.ProcessSupervisor = .{}, persisted_store: ?background_store.Store = null, owned_session_capability: ?*session_child_store.SessionChildCapability = null, borrowed_session_capability: ?*session_child_store.SessionChildCapability = null, source_session_id: ?[]u8 = null, retained_source_capabilities: std.ArrayList(RetainedSourceCapability) = .empty, retained_indeterminate_identities: std.ArrayList(background_launch_identity.Identity) = .empty, prepared_launch_reservations: usize = 0, owned_children: std.ArrayList(OwnedBackgroundChild) = .empty, watchers: std.ArrayList(BackgroundWatcherHandle) = .empty, stop_requested: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), last_refresh_ms: i64 = 0, pub fn init( process_provider: background_process_provider.Provider, ) BackgroundRuntime { return .{ .process_provider = process_provider }; } pub fn processProvider( self: *const BackgroundRuntime, ) background_process_provider.Provider { return self.process_provider; } pub fn deinit(self: *BackgroundRuntime, alloc: Allocator) void { if (self.source_session_id) |source_session_id| { self.retryDegradedRecordsForSource( alloc, source_session_id, ); } self.requestStop(); self.pruneWatchers(alloc, true); self.terminateOwnedUnrecordedProcesses(alloc); self.watchers.deinit(alloc); for (self.owned_children.items) |*owned| { owned.process.forget(); } self.owned_children.deinit(alloc); if (self.persisted_store) |*store| store.deinit(alloc); for (self.retained_source_capabilities.items) |*retained| { retained.deinit(alloc); } self.retained_source_capabilities.deinit(alloc); for (self.retained_indeterminate_identities.items) |*identity| { identity.deinit(alloc); } self.retained_indeterminate_identities.deinit(alloc); if (self.owned_session_capability) |capability| { capability.deinit(); alloc.destroy(capability); } if (self.source_session_id) |source_session_id| { alloc.free(source_session_id); } self.supervisor.deinit(alloc); self.* = .{}; } pub fn enablePersistence(self: *BackgroundRuntime, alloc: Allocator, background_dir: []const u8) !void { const session_dir_path = std.fs.path.dirname(background_dir) orelse return error.SessionChildStoreFailed; const source_session_id = std.fs.path.basename(session_dir_path); const capability = try alloc.create( session_child_store.SessionChildCapability, ); var capability_owned = true; errdefer if (capability_owned) alloc.destroy(capability); capability.* = try session_child_store.SessionChildCapability.initLegacyBackgroundRoutes( alloc, background_dir, .writable, ); errdefer if (capability_owned) capability.deinit(); self.installPersistence( alloc, capability, source_session_id, true, ) catch |err| { if (self.activeSessionCapability() == capability) { capability_owned = false; } return err; }; capability_owned = false; } pub fn enableManagedPersistence( self: *BackgroundRuntime, alloc: Allocator, capability: *session_child_store.SessionChildCapability, source_session_id: []const u8, ) !void { try self.installPersistence( alloc, capability, source_session_id, false, ); } fn installPersistence( self: *BackgroundRuntime, alloc: Allocator, capability: *session_child_store.SessionChildCapability, source_session_id: []const u8, owns_capability: bool, ) !void { var store = background_store.Store.initManaged(capability); const owned_source_session_id = try alloc.dupe( u8, source_session_id, ); errdefer alloc.free(owned_source_session_id); if (self.source_session_id) |existing_source_session_id| { if (std.mem.eql( u8, existing_source_session_id, owned_source_session_id, ) and self.activeSessionCapability() == capability) { alloc.free(owned_source_session_id); return; } self.invalidateSourceAuthority( alloc, existing_source_session_id, ); } self.mutex.lockUncancelable(io_mod.getIo()); errdefer self.mutex.unlock(io_mod.getIo()); const next_id = try store.nextId(); if (self.persisted_store) |*existing| existing.deinit(alloc); if (self.source_session_id) |closing_source_session_id| { for (self.supervisor.tasks.items) |*task| { const task_source = task.source_session_id orelse continue; if (!std.mem.eql( u8, task_source, closing_source_session_id, )) continue; task.record_authority = .none; } } if (self.owned_session_capability) |existing| { existing.deinit(); alloc.destroy(existing); } self.borrowed_session_capability = null; if (self.source_session_id) |existing| alloc.free(existing); self.persisted_store = store; if (owns_capability) { self.owned_session_capability = capability; } else { self.owned_session_capability = null; self.borrowed_session_capability = capability; } self.source_session_id = owned_source_session_id; self.supervisor.next_background_process_id = @max(self.supervisor.next_background_process_id, next_id); for (self.supervisor.tasks.items) |*task| { const task_source = task.source_session_id orelse continue; const stable_id = task.background_record_id orelse continue; if (!std.mem.eql( u8, task_source, owned_source_session_id, )) continue; if (store.loadByStableId(alloc, stable_id)) |record| { var current = record; defer current.deinit(alloc); const token = current.process_token orelse continue; const expected = process_supervisor.ProcessInstanceToken.parse( token, ) catch continue; const token_match = self.process_provider.matchToken( alloc, current.pid, expected, ); switch (token_match) { .matched => { task.record_authority = .{ .writable = capability, }; }, .missing, .mismatched => { task.record_authority = .{ .writable = capability, }; if (task.state == .running) { task.state = .stale; task.expect_url = false; task.exit_code = null; } }, .unavailable => {}, } } else |_| {} } const snapshots = self.supervisor.snapshotTasks(alloc) catch |err| { if (self.persisted_store) |*existing| existing.deinit(alloc); self.persisted_store = null; return err; }; self.mutex.unlock(io_mod.getIo()); defer snapshots.deinit(alloc); self.saveSnapshotsOrDisablePersistence(alloc, "enable background persistence", snapshots.items); } pub fn invalidateSourceAuthority( self: *BackgroundRuntime, alloc: Allocator, source_session_id: []const u8, ) void { self.retryDegradedRecordsForSource( alloc, source_session_id, ); self.mutex.lockUncancelable(io_mod.getIo()); defer self.mutex.unlock(io_mod.getIo()); for (self.supervisor.tasks.items) |*task| { const task_source = task.source_session_id orelse continue; if (!std.mem.eql( u8, task_source, source_session_id, )) continue; task.record_authority = .none; } } pub fn detachManagedPersistence( self: *BackgroundRuntime, alloc: Allocator, source_session_id: []const u8, ) void { const current = self.source_session_id orelse return; if (!std.mem.eql(u8, current, source_session_id)) return; self.invalidateSourceAuthority(alloc, source_session_id); self.mutex.lockUncancelable(io_mod.getIo()); defer self.mutex.unlock(io_mod.getIo()); if (self.persisted_store) |*store| store.deinit(alloc); self.persisted_store = null; if (self.owned_session_capability) |capability| { capability.deinit(); alloc.destroy(capability); } self.owned_session_capability = null; self.borrowed_session_capability = null; if (self.source_session_id) |owned| alloc.free(owned); self.source_session_id = null; } fn activeSessionCapability( self: *BackgroundRuntime, ) ?*session_child_store.SessionChildCapability { return self.borrowed_session_capability orelse self.owned_session_capability; } fn retryDegradedRecordsForSource( self: *BackgroundRuntime, alloc: Allocator, source_session_id: []const u8, ) void { self.mutex.lockUncancelable(io_mod.getIo()); const snapshots = self.supervisor.snapshotTasks(alloc) catch { self.mutex.unlock(io_mod.getIo()); return; }; self.mutex.unlock(io_mod.getIo()); defer snapshots.deinit(alloc); for (snapshots.items) |task_snapshot| { const task_source = task_snapshot.source_session_id orelse continue; if (!std.mem.eql( u8, task_source, source_session_id, )) continue; if (task_snapshot.record_persistence != .initial_record_degraded and task_snapshot.record_persistence != .record_update_degraded) { continue; } self.saveDurableSnapshotIfAuthorized( alloc, "final source authority retry", task_snapshot, ); } } pub fn prepareBackgroundLaunch( self: *BackgroundRuntime, alloc: Allocator, policy: BackgroundLaunchPolicy, ) !PreparedBackgroundLaunch { self.mutex.lockUncancelable(io_mod.getIo()); var locked = true; errdefer if (locked) self.mutex.unlock(io_mod.getIo()); const reserved_retention_slots = std.math.add( usize, self.prepared_launch_reservations, 1, ) catch return error.BackgroundIdentityUnavailable; try self.owned_children.ensureUnusedCapacity( alloc, reserved_retention_slots, ); try self.retained_indeterminate_identities.ensureUnusedCapacity( alloc, reserved_retention_slots, ); const display_id = try self.supervisor.reserveDisplayId(); errdefer self.supervisor.releaseDisplayId(display_id); var identity: background_launch_identity.Identity = undefined; switch (policy) { .process_local_long_lived => { identity = .{ .process_local_long_lived = .{ .display_id = display_id, } }; }, .durable_long_lived, .saved_headless => { const source_session_id = self.source_session_id orelse return error.BackgroundPersistenceUnavailable; const store = self.persisted_store orelse return error.BackgroundPersistenceUnavailable; const stable_id = try self.generateStableRecordIdLocked( alloc, store, source_session_id, ); const owned_source_session_id = try alloc.dupe( u8, source_session_id, ); identity = switch (policy) { .durable_long_lived => .{ .durable_long_lived = .{ .display_id = display_id, .source_session_id = owned_source_session_id, .background_record_id = stable_id, }, }, .saved_headless => .{ .saved_headless = .{ .display_id = display_id, .source_session_id = owned_source_session_id, .background_record_id = stable_id, }, }, .process_local_long_lived => unreachable, }; }, } self.prepared_launch_reservations += 1; self.mutex.unlock(io_mod.getIo()); locked = false; errdefer self.releasePreparedLaunchReservation(); errdefer identity.deinit(alloc); const output = switch (policy) { .process_local_long_lived => try background_launch_output.prepareExternal(alloc), .durable_long_lived, .saved_headless => blk: { const capability = self.activeSessionCapability() orelse return error.BackgroundPersistenceUnavailable;