返回 DeepSeek-Reasonix
session_service_shutdown.go
根目录 / desktop / session_service_shutdown.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "log/slog"
7
8 "reasonix/internal/session"
9 )
10
11 // Run after controllers and lifecycle barriers release their bindings.
12 func (a *App) closeSessionServices() {
13 if err := a.closeSessionServicesResult(); err != nil {
14 slog.Warn("desktop: close session service", "err", err)
15 }
16 }
17
18 func (a *App) closeSessionServicesResult() error {
19 a.closeHistoryReaders()
20 a.desktopSessions.readSnapshots.close()
21 a.sessionServicesMu.Lock()
22 services := make([]*session.Service, 0, len(a.sessionServices))
23 for _, service := range a.sessionServices {
24 services = append(services, service)
25 }
26 for _, service := range a.historicalSessionServices {
27 services = append(services, service)
28 }
29 a.sessionServicesMu.Unlock()
30 var failures error
31 for _, service := range services {
32 if err := service.Shutdown(context.Background()); err != nil {
33 if onlyReleasedBindingErrors(err) {
34 slog.Warn("desktop: released leaked session bindings during shutdown", "err", err)
35 } else {
36 failures = errors.Join(failures, err)
37 }
38 }
39 }
40 return failures
41 }
42
43 // Shutdown may release an idle leaked binding successfully. Do not turn that
44 // warning into an unresolvable exit failure, or swallow errors joined with it.
45 func onlyReleasedBindingErrors(err error) bool {
46 if joined, ok := err.(interface{ Unwrap() []error }); ok {
47 for _, child := range joined.Unwrap() {
48 if !onlyReleasedBindingErrors(child) {
49 return false
50 }
51 }
52 return true
53 }
54 if wrapped := errors.Unwrap(err); wrapped != nil {
55 return onlyReleasedBindingErrors(wrapped)
56 }
57 return errors.Is(err, session.ErrRuntimeBound)
58 }
59
59 lines GO