Package · thread

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:

php
$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:

php
$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:

src/BatchJob.php
<?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:

php
$thread->pause();   // frozen by the OS — cannot be caught
// ... free up CPU for something else ...
$thread->resume();  // continues where it left off