pub struct JobManager { /* private fields */ }Expand description
Manager for background jobs.
Implementations§
Source§impl JobManager
impl JobManager
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new job manager.
On construction, best-effort prunes stale job output files left by previously crashed kaish processes. All errors are intentionally ignored — startup cleanup is opportunistic and must never prevent the manager from being created (silent-fallback rule: the only case where silent is correct is read-only / cleanup-only paths with no data loss risk).
§Scoping decision
All sessions share a single /tmp/kaish/jobs/ directory. Filenames embed
the OS PID that wrote them (session_S_job_J.PID.txt). Files from the
current process are never touched here — only files whose embedded PID
refers to a dead process are removed. On Linux we check /proc/{pid} for
existence; on other platforms we skip the prune rather than guess.
Sourcepub fn set_persist_output_files(&self, on: bool)
pub fn set_persist_output_files(&self, on: bool)
Toggle whether completed jobs persist their output to a host temp file.
Disable this for a hermetic / read-only kernel: the host write in
Job::write_output_file uses std::fs directly and so bypasses the
VFS (and any read-only mount). Live output stays available in-memory via
the VFS streams (/v/jobs/{id}/stdout), so nothing is lost in-process.
Must be set before jobs are spawned — the flag is stamped onto each job at registration time, not consulted at completion.
Sourcepub fn persist_output_files(&self) -> bool
pub fn persist_output_files(&self) -> bool
Whether completed jobs persist their output to a host temp file.
Sourcepub async fn spawn<F>(&self, command: String, future: F) -> JobId
pub async fn spawn<F>(&self, command: String, future: F) -> JobId
Spawn a new background job from a future.
The job is inserted into the map synchronously before returning,
guaranteeing it’s immediately queryable via exists() or get().
Sourcepub async fn register(&self, command: String, rx: Receiver<ExecResult>) -> JobId
pub async fn register(&self, command: String, rx: Receiver<ExecResult>) -> JobId
Spawn a job that’s already running and communicate via channel.
Sourcepub async fn register_with_streams(
&self,
command: String,
rx: Receiver<ExecResult>,
stdout: Arc<BoundedStream>,
stderr: Arc<BoundedStream>,
) -> JobId
pub async fn register_with_streams( &self, command: String, rx: Receiver<ExecResult>, stdout: Arc<BoundedStream>, stderr: Arc<BoundedStream>, ) -> JobId
Register a job with attached output streams.
The streams provide live access to job output via /v/jobs/{id}/stdout and /stderr.
Sourcepub async fn wait(&self, id: JobId) -> Option<ExecResult>
pub async fn wait(&self, id: JobId) -> Option<ExecResult>
Wait for a specific job to complete.
The job’s pending awaitable (its task handle or result channel) is taken
out of the map under the lock, then the lock is released before
awaiting completion. Holding the jobs mutex across the await would
block every other job operation (spawn/register/list/status/kill) for the
whole duration of the job — so a nested & started under a parked
wait %N would deadlock. The lock is re-acquired only to finalize
(persist output, cache the result).
Sourcepub async fn wait_all(&self) -> Vec<(JobId, ExecResult)>
pub async fn wait_all(&self) -> Vec<(JobId, ExecResult)>
Wait for all jobs to complete, returning results in completion order.
Sourcepub async fn running_count(&self) -> usize
pub async fn running_count(&self) -> usize
Get the number of running jobs.
Sourcepub async fn reap_finished(&self) -> Vec<JobInfo>
pub async fn reap_finished(&self) -> Vec<JobInfo>
Remove completed jobs from tracking and clean up their temp files, returning info for each job removed.
A latched job is “done” but its cached result holds the only
LatchRequest for the gated operation — reaping it would silently
destroy the pending confirmation (GH #96). It stays until confirmed
or explicitly discarded (kill --discard %N).
Shared by jobs --cleanup (which only needs a count) and the REPL’s
pre-prompt notification (GH #131, which needs the id/command/status of
each job so it can print [N]+ Done ... before reaping it) — one rule
for “is this job safe to reap”, not two copies that could drift.
Sourcepub async fn cleanup(&self)
pub async fn cleanup(&self)
Remove completed jobs from tracking and clean up their temp files.
See reap_finished for the latch-safety rule;
this is the count-only form jobs --cleanup reports.
Sourcepub async fn is_latched(&self, id: JobId) -> bool
pub async fn is_latched(&self, id: JobId) -> bool
Whether the job’s cached result is a pending confirmation gate
(JobStatus::Latched). Consumers that would drop the job (kill,
cleanup paths) check this so a latch is never destroyed silently.
Sourcepub async fn get_command(&self, id: JobId) -> Option<String>
pub async fn get_command(&self, id: JobId) -> Option<String>
Get the command string for a job.
Sourcepub async fn get_status_string(&self, id: JobId) -> Option<String>
pub async fn get_status_string(&self, id: JobId) -> Option<String>
Get the status string for a job (for /v/jobs/{id}/status).
Sourcepub async fn get_latch(&self, id: JobId) -> Option<LatchRequest>
pub async fn get_latch(&self, id: JobId) -> Option<LatchRequest>
Get a gated job’s pending confirmation-latch request (for
/v/jobs/{id}/latch and any embedder reaching a backgrounded gate).
Some(None) vs None distinguishes “job exists, not latched” from
“no such job”; jobfs flattens both to an empty node body. GH #96.
Sourcepub async fn read_stdout(&self, id: JobId) -> Option<Vec<u8>>
pub async fn read_stdout(&self, id: JobId) -> Option<Vec<u8>>
Read stdout stream content for a job.
Returns None if the job doesn’t exist or has no attached stream.
Sourcepub async fn read_stderr(&self, id: JobId) -> Option<Vec<u8>>
pub async fn read_stderr(&self, id: JobId) -> Option<Vec<u8>>
Read stderr stream content for a job.
Returns None if the job doesn’t exist or has no attached stream.
Sourcepub async fn register_stopped(
&self,
command: String,
pid: u32,
pgid: u32,
) -> JobId
pub async fn register_stopped( &self, command: String, pid: u32, pgid: u32, ) -> JobId
Register a stopped job (from Ctrl-Z on a foreground process).
Sourcepub async fn stop_job(&self, id: JobId, pid: u32, pgid: u32)
pub async fn stop_job(&self, id: JobId, pid: u32, pgid: u32)
Mark a job as stopped with its process info.
Sourcepub async fn resume_job(&self, id: JobId)
pub async fn resume_job(&self, id: JobId)
Mark a stopped job as resumed.
Sourcepub async fn last_stopped(&self) -> Option<JobId>
pub async fn last_stopped(&self) -> Option<JobId>
Get the most recently stopped job.
Sourcepub async fn get_process_info(&self, id: JobId) -> Option<(u32, u32)>
pub async fn get_process_info(&self, id: JobId) -> Option<(u32, u32)>
Get process info (pid, pgid) for a job.
Sourcepub async fn set_cancel_token(&self, id: JobId, token: CancellationToken)
pub async fn set_cancel_token(&self, id: JobId, token: CancellationToken)
Record the cancellation token of the fork running a background job, so
kill %N can stop the job even when it has no OS process group of its
own (e.g. a pure builtin like sleep &).
Sourcepub async fn cancel(&self, id: JobId) -> bool
pub async fn cancel(&self, id: JobId) -> bool
Cancel a job by its token. Returns true if a token was recorded and
cancelled. The cancellation cascade stops in-process builtin futures and
SIGTERM→SIGKILLs any external children’s process groups.
Sourcepub async fn add_pgid(&self, id: JobId, pgid: u32)
pub async fn add_pgid(&self, id: JobId, pgid: u32)
Record a process group spawned while running a background job. Lets
kill -<sig> %N deliver an arbitrary signal directly to the real
processes. Deduplicated (a job may spawn several externals).
Sourcepub async fn job_pgids(&self, id: JobId) -> Vec<u32>
pub async fn job_pgids(&self, id: JobId) -> Vec<u32>
The process groups recorded for a job (empty for a pure-builtin job).
Includes the legacy single pgid recorded for stopped jobs (Ctrl-Z),
so kill %N signals a stopped foreground job’s group too.
Sourcepub async fn remove(&self, id: JobId)
pub async fn remove(&self, id: JobId)
Remove a job from tracking.
NOTE: this bypasses the latch guard — a caller that might hit a
latched job must check is_latched first (see
the kill builtin), or the job’s pending confirmation is destroyed
with it. cleanup() is the latch-safe bulk path.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for JobManager
impl !RefUnwindSafe for JobManager
impl !UnwindSafe for JobManager
impl Send for JobManager
impl Sync for JobManager
impl Unpin for JobManager
impl UnsafeUnpin for JobManager
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
Source§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T, S> SpanWrap<S> for Twhere
S: WrappingSpan<T>,
impl<T, S> SpanWrap<S> for Twhere
S: WrappingSpan<T>,
Source§fn with_span(self, span: S) -> <S as WrappingSpan<Self>>::Spanned
fn with_span(self, span: S) -> <S as WrappingSpan<Self>>::Spanned
WrappingSpan::make_wrapped to wrap an AST node in a span.