Skip to content

Supervised Daemon Threads for Winter Boot Applications

A daemon thread is a long-running background process that starts with your application and runs for as long as it does. Despite the name, it is not an OS thread — it is a child process supervised by the framework. Use one for work that must always be running: polling a queue, tailing a stream, monitoring an external system, or processing database rows in a loop. If a daemon exits unexpectedly (an unhandled exception or running out of memory), the framework restarts it, so you do not need your own process supervisor.

Apply #[DaemonThread] to a class that extends ServerWorkerProcess. The attribute 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”

#[Autowired] 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) {
// Fulfill the order …
$this->notifier->sendConfirmation($order['customer_email'], $order['id']);
}
\Co::sleep(3);
}
}
}

The framework monitors every daemon process. If a daemon exits — an uncaught exception, running out of memory, or any other crash — it is restarted automatically. 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

Coordinate other parts of the system — purging stale sessions, refreshing shared caches, or watching worker health.