qsu

Design Notes
Login

Design Notes

qsu's primary function is to provide a service runtime layer that sits between the operating system's service subsystem and the application server code. It provides abstractions that allow a server to behave in a unified way, regardless of the actual service subsystem type (it even strives to support the same interface and semantics when running as a foreground process).

In order to write a service application, the developer first chooses a runtime to integrate against. This basically refers to choosing whether the service runs async or non-async code. Next they implement a runtime type specific trait, which has three methods init(), run() and shutdown().

Some service subsystems have special "startup" and "shutdown" phases. The service handler's trait methods init() and shutdown() correspond to these phases, and qsu will report (as appropriate) to the service subsystem that these phases have been entered as the appropriate callback methods are called.

An application also needs to set up a service event handler. This is a closure that will receive events from the service subsystem. When running as a foreground process, qsu simulates the same behavior so the application will receive the same events. Specifically, when a Window Service is stopped, a SvcEvt::Shutdown(_) event will be received by the event handler. When a service application is run in foreground mode, pressing Ctrl+C or Ctrl+Break will cause the same SvcEvt::Shutdown(_) to be sent.

Once the service handler trait has been implemented and the service event closure has been created, the qsu service runtime is called, passing along the two handlers. At this point qsu will initialize logging and then call the service handler's init() implementation, and if successful it will call its run() implementation and wait until it returns before calling the shutdown() impelementation and then finally terminate the runtime.

Applications must themselves translate shutdown events received by the service event handler to an actual termination of the service application. This is typically done using channels, or cancellation tokens.

Errors

There are several application callbacks in qsu; primarily in the service application runtime, but also in the argument parser. It is recognized that service applications that encounter fatal errors may want to return the application-specific error back to the original caller (for instance to return different appropriate exit codes to the calling shell). To allow this callbacks return an error type CbErr<ApEr> which is generic over an application-defined type used to carry the application error.

However, there are parts of qsu that doesn't have need for this, even parts that become needlessly complicated having to support the generic parameter, so there's an Error type that does not carry the ApEr generic.

CbErr can carry the Error type within one of its variants.

ToDo