Graceful shutdown
Stop background work cleanly: wait with a deadline, ask the task to finish, and force it
only when it won’t. Signals require the posix extension.
Wait with a timeout
join() blocks forever by default. Pass a timeout (seconds) to cap the wait — it returns
null if the deadline passes before the task finishes:
$exitCode = $thread->join(timeout: 30);
if ($exitCode === null) {
// Still running after 30s — decide what to do next
echo "Timed out; asking it to stop...\n";
} elseif ($exitCode === 0) {
echo "Finished cleanly.\n";
} else {
echo "Failed with exit code {$exitCode}.\n";
}join() return values
int exit code on exit · null on timeout · -1 if the thread was never started. Full
table in the API reference.
Ask nicely, then force
The clean shutdown pattern: send SIGTERM (catchable, lets the task clean up), give it a
moment, and escalate to SIGKILL (uncatchable) only if it ignores you:
$thread->terminate(); // SIGTERM — please wrap up
if ($thread->join(timeout: 5) === null) {
echo "Ignored SIGTERM; killing.\n";
$thread->kill(); // SIGKILL — cannot be caught or ignored
$thread->join();
}| Method | Signal | Catchable? | Use when |
|---|---|---|---|
interrupt() |
SIGINT |
yes | Ctrl+C-style interrupt |
terminate() |
SIGTERM |
yes | Ask for a clean shutdown |
kill() |
SIGKILL |
no | Last resort after terminate() |
Handle SIGTERM inside the task
For terminate() to be graceful, the task has to listen for it and stop at a safe point:
<?php
use Flytachi\Winter\Thread\Runnable;
class BatchJob implements Runnable
{
public function run(array $args): void
{
pcntl_async_signals(true);
$stop = false;
pcntl_signal(SIGTERM, function () use (&$stop) {
$stop = true; // don't do heavy work in a signal handler
});
foreach ($this->chunks() as $chunk) {
if ($stop) {
$this->checkpoint(); // save progress and exit cleanly
return;
}
$this->process($chunk);
}
}
}Pause and resume
Need to suspend a process without ending it? pause() sends SIGSTOP (the OS freezes it;
it stays in the process table, so isAlive() is still true) and resume() sends
SIGCONT:
$thread->pause(); // frozen by the OS — cannot be caught
// ... free up CPU for something else ...
$thread->resume(); // continues where it left offRelated
- API reference — every signal method
- Signals & exit codes — the full signal table
- PID reuse & signals — why raw-PID signalling is risky