| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- package xpc
- import (
- "context"
- "fmt"
- "os"
- "os/signal"
- "reflect"
- "syscall"
- "time"
- )
- // Implements is a struct that component implementations must embed.
- // T is the component interface.
- type Implements[T any] struct{}
- // Register is used by generated code to register components.
- func Register[Intf any, Impl any](newFunc func() any) {
- registerComponent((*Intf)(nil), (*Impl)(nil), newFunc)
- }
- // Options configuration for the XPC runtime
- type Options struct {
- // TLSCertFile path to the TLS certificate file (for cluster mode)
- TLSCertFile string
- // TLSKeyFile path to the TLS key file (for cluster mode)
- TLSKeyFile string
- }
- // Run starts the XPC application.
- // app must be a function with the signature: func(context.Context, Component...) error
- // It handles OS signals (SIGINT, SIGTERM) to cancel the context passed to the app.
- func Run(ctx context.Context, app any, opts ...Options) error {
- // Parse options
- var opt Options
- if len(opts) > 0 {
- opt = opts[0]
- }
-
- rtConfig := runtimeConfig{
- TLSCertFile: opt.TLSCertFile,
- TLSKeyFile: opt.TLSKeyFile,
- }
- appVal := reflect.ValueOf(app)
- appType := appVal.Type()
- if appType.Kind() != reflect.Func {
- return fmt.Errorf("app must be a function, got %v", appType)
- }
- if appType.NumIn() < 1 || appType.In(0) != reflect.TypeOf((*context.Context)(nil)).Elem() {
- return fmt.Errorf("first argument of app must be context.Context")
- }
- if appType.NumOut() != 1 || appType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
- return fmt.Errorf("app must return a single error")
- }
- // Setup signal handling
- ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
- defer cancel()
- // Initialize Runtime based on configuration
- var rt runtime
- if os.Getenv("XPC_MODE") == "cluster" {
- rt = newClusterRuntime(rtConfig)
- } else {
- rt = newLocalRuntime(rtConfig)
- }
- // Instantiate requested root components
- args := []reflect.Value{reflect.ValueOf(ctx)}
- for i := 1; i < appType.NumIn(); i++ {
- argType := appType.In(i)
- comp, err := rt.getInstance(ctx, argType)
- if err != nil {
- return fmt.Errorf("failed to get instance for argument %d (%v): %w", i, argType, err)
- }
- args = append(args, reflect.ValueOf(comp))
- }
- // Execute Application
- results := appVal.Call(args)
-
- // Graceful Shutdown
- shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer shutdownCancel()
-
- if err := rt.shutdown(shutdownCtx); err != nil {
- fmt.Fprintf(os.Stderr, "xpc: shutdown error: %v\n", err)
- }
- errVal := results[0]
- if !errVal.IsNil() {
- return errVal.Interface().(error)
- }
- return nil
- }
|