Skip to content

Supervised Daemon Threads for Winter Boot Applications

A Daemon Thread in Winter Boot is a long-running background process that starts with your application and keeps running for its entire lifetime. Despite the name, it is not an OS thread or a Java-style thread — it is a dedicated Swoole child process supervised by the framework. Daemon threads are the right tool for work that must always be running: polling a queue, tailing a log stream, monitoring external systems, or processing rows from a database table in a continuous loop. The framework automatically restarts a daemon if it exits unexpectedly due to an out-of-memory error or an unhandled exception, so you never need to wire up your own process supervisor for these use cases.

Apply #[DaemonThread] to a class that extends ServerWorkerProcess. The annotation accepts two optional parameters:

  • name string (default: Class name) A human-readable label for the daemon process, used in logs and process tables.

  • coreSize int (default: 1) Number of daemon process instances to start. Set to 0 (or any negative value) to disable the daemon entirely — useful for feature-flagging in different environments.

SomeBackendProcessing.php
use dev\winterframework\stereotype\cli\DaemonThread;
use dev\winterframework\io\process\ServerWorkerProcess;
use dev\winterframework\io\process\ProcessType;
#[DaemonThread(name: 'my-processor', coreSize: 2)]
class SomeBackendProcessing extends ServerWorkerProcess
{
// …
}

Every daemon class must extend ServerWorkerProcess and implement three members:

Member Visibility Description
getProcessType(): int public abstract Returns a ProcessType constant identifying the process role.
getProcessId(): string public abstract Returns a unique string identifier for this daemon instance (used by the supervisor).
run(): void protected abstract Contains the daemon’s main loop. Called once at startup; the process lives as long as this method runs.

getProcessType() should return one of the constants defined on the ProcessType interface. For custom daemon threads, use ProcessType::OTHER unless you are extending a built-in framework process type.

View all ProcessType constants
Constant Value Meaning
ProcessType::OTHER 0 General-purpose background process.
ProcessType::MASTER 1 Framework master process.
ProcessType::MANAGER 2 Framework manager process.
ProcessType::HTTP_WORKER 3 HTTP request worker.
ProcessType::TASK_WORKER 4 Generic task worker.
ProcessType::ASYNC_WORKER 5 Async execution worker (see Async Tasks).
ProcessType::SCHED_WORKER 6 Scheduling worker (see Scheduling).
ProcessType::KV_MONITOR 7 Key-value store monitor.
ProcessType::QUEUE_MONITOR 8 Queue monitor.
ProcessType::KV_SERVER 9 Key-value server process.
ProcessType::QUEUE_SERVER 10 Queue server process.

The following example polls a database table for pending rows, processes each one, and then sleeps for five seconds before repeating.

SomeBackendProcessing.php
use dev\winterframework\stereotype\cli\DaemonThread;
use dev\winterframework\io\process\ServerWorkerProcess;
use dev\winterframework\io\process\ProcessType;
use dev\winterframework\stereotype\Autowired;
use dev\winterframework\pdbc\PdbcTemplate;
#[DaemonThread]
class SomeBackendProcessing extends ServerWorkerProcess
{
#[Autowired]
protected PdbcTemplate $pdbc;
public function getProcessType(): int
{
return ProcessType::OTHER;
}
public function getProcessId(): string
{
return 'my-backend-processor';
}
protected function run(): void
{
while (1) {
$to_process = $this->pdbc->queryForList(
"SELECT * FROM SOME_TABLE WHERE STATUS = 'PENDING'"
);
foreach ($to_process as $row) {
// process each pending row …
}
// sleep for 5 seconds (Swoole coroutine-safe)
\Co::sleep(5);
}
}
}

Dependency Injection Inside Daemon Threads

Section titled “Dependency Injection Inside Daemon Threads”

The #[Autowired] annotation works inside daemon thread classes just as it does in regular service beans. The framework performs property injection before calling run(), so any container-managed bean — database templates, configuration properties, other services — is available as soon as your loop starts.

OrderProcessingDaemon.php
use dev\winterframework\stereotype\cli\DaemonThread;
use dev\winterframework\io\process\ServerWorkerProcess;
use dev\winterframework\io\process\ProcessType;
use dev\winterframework\stereotype\Autowired;
use dev\winterframework\pdbc\PdbcTemplate;
#[DaemonThread(name: 'order-processor', coreSize: 1)]
class OrderProcessingDaemon extends ServerWorkerProcess
{
#[Autowired]
private PdbcTemplate $pdbc;
#[Autowired]
private NotificationService $notifier;
public function getProcessType(): int
{
return ProcessType::OTHER;
}
public function getProcessId(): string
{
return 'order-processor';
}
protected function run(): void
{
while (1) {
$orders = $this->pdbc->queryForList(
"SELECT * FROM ORDERS WHERE STATUS = 'NEW' LIMIT 10"
);
foreach ($orders as $order) {
// Fulfil the order …
$this->notifier->sendConfirmation($order['customer_email'], $order['id']);
}
\Co::sleep(3);
}
}
}

The Winter Boot framework monitors every registered daemon process. If a daemon exits — whether due to an uncaught exception, an out-of-memory error, or any other crash — the framework automatically restarts it. You do not need an external process manager (like Supervisor or systemd) to keep daemons alive.

Monitoring & Alerting

Continuously poll metrics, health endpoints, or infrastructure telemetry. Fire alerts when thresholds are exceeded without adding latency to user-facing requests.

Stream Processing

Consume messages from a queue, Kafka topic, or event stream in a tight loop. Process and acknowledge messages entirely outside the HTTP worker pool.

Background Jobs

Pick up pending database rows, trigger batch reports, or run data-migration steps continuously in the background without blocking API responses.

Supervision & Coordination

Act as a coordinator for other subsystems — reaping stale sessions, refreshing distributed caches, or enforcing rate limits across worker processes.