:title
SERVICE

:sig
function SERVICE:START(props : dval)
function SERVICE:STOP(props : dval)
function SERVICE:DATA(props : dval)
function SERVICE:EXIT(props : dval)
function SERVICE:ACCEPT(props : dval)
function SERVICE:NAME(props : dval)

:content
`SERVICE` handlers are the events of a service: a unit that keeps running between requests and owns its own I/O, such as child processes, pipes, sockets, and timers. Bearer runs each handler as one short, bounded call into a workspace that lives as long as the service, so the unit's variables carry the service state from one event to the next.

A service handler is always named. `START`, `STOP`, `DATA`, `EXIT`, and `ACCEPT` are the runtime's events. Every other name is a signal that `service_send()` or `service_every()` delivers. The name follows the `TASK:NAME` rule: a letter or underscore, then letters, digits, and underscores.

## Events

| Handler | `props` | When |
| --- | --- | --- |
| `SERVICE:START` | the start arguments | once, after the workspace is up |
| `SERVICE:STOP` | `{}` | before the worker exits; optional |
| `SERVICE:DATA` | `port`, `stream`, `data` | a port became readable |
| `SERVICE:EXIT` | `port`, `status` | a spawned child exited, after its last `DATA` |
| `SERVICE:ACCEPT` | `port`, `connection` | a listening port accepted a connection |
| `SERVICE:NAME` | the posted props | a `NAME` signal arrived |

Handlers run one at a time, in event order. A service never needs a lock.

## State

Service state lives in unit variables. A top-level `var` is initialized when the workspace comes up and keeps its value across every handler call until the service stops:

    var agents := {}

    function SERVICE:START(props : dval)
    {
    	for i := 0..s64(props.count) { spawn_agent() }
    }

    function SERVICE:EXIT(props : dval)
    {
    	delete(agents, string(props.port))
    	service_send(request.unit, "respawn", {}, 2000)
    }

## The request

Like every handler, a service handler binds `request` implicitly. A service has no HTTP request, so this one is synthetic: no session, no body, no form. It carries the start arguments in `request.props`, the unit path in `request.unit`, and the server values, and it lets request builtins such as `ws_send()` and `unit_call()` work unchanged. It is bound once for the life of the service. Keep state in unit variables, not in request keys.

## Lifetime

Each handler call runs under the invocation deadline. A handler that hangs is interrupted, the event is dropped with an operator log line, and the service continues with the next event. A signal the unit has no handler for is dropped and logged. Printed output goes to the operator log; a service has no response.

`STOP` jumps ahead of queued messages. Once it returns, the worker exits and whatever was still queued is dropped. A service that exits without a stop is restarted with increasing delays, and `service_status()` counts the restarts. `service_stop()` keeps a service down until the next `service_start()` or `service_send()`.

A service does not restart when its source changes. Stop and start it, which recompiles the unit.

:content
API examples on this page are handler bodies; `started` is a unit variable of the service.

:errors
A handler that traps or exceeds the invocation deadline drops that one event and is logged. A `START` that fails is logged and the service keeps running. A worker that cannot compile its unit or build its workspace exits, which the supervisor records as a crash.

:example capy service
started = s64(props.count)

:example capy service react to output from a child process
ws_send(string(props.data), false, "agents")

:see-group
handlers

:see
capy-12-services
handler/component
