返回 DeepSeek-Reasonix
session_catalog_lifecycle.go
根目录 / desktop / session_catalog_lifecycle.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "log/slog"
7 "os"
8 "strings"
9
10 "reasonix/internal/history"
11 "reasonix/internal/projectiondb"
12 "reasonix/internal/sessioncatalog"
13 "reasonix/internal/taskcatalog"
14 )
15
16 func (a *App) runSessionCatalog(ctx context.Context, initialReconcileDone chan struct{}, metadataRequests <-chan struct{}) {
17 initialReconcileFinished := false
18 defer func() {
19 if !initialReconcileFinished {
20 close(initialReconcileDone)
21 }
22 }()
23 path := sessioncatalog.DefaultPath()
24 freshGeneration := false
25 if strings.TrimSpace(path) != "" {
26 _, statErr := os.Stat(path)
27 freshGeneration = errors.Is(statErr, os.ErrNotExist)
28 }
29 targets := a.sessionCatalogTargets()
30 history.RegisterCatalogRoots(historyCatalogRoots(targets))
31 projects := loadProjectsFile()
32 taskcatalog.RegisterSharedProject(globalWorkspaceRoot(), projects.GlobalTitle)
33 for _, project := range projects.Projects {
34 taskcatalog.RegisterSharedProject(project.Root, projectDisplayName(project))
35 }
36 var revisionFloor uint64
37 deferredIntegrity := true
38 for ctx.Err() == nil {
39 catalog, err := sessioncatalog.Open(ctx, sessioncatalog.Options{
40 Path: path, MetadataOnly: true, StartPaused: true,
41 DeferredMetadataIntegrity: deferredIntegrity, RevisionFloor: revisionFloor,
42 Maintenance: &a.historyMaintenance,
43 OnDiscovery: func(event sessioncatalog.DiscoveryEvent) {
44 slog.Info("desktop: history discovery", "root", event.Root, "sequence", event.Sequence,
45 "phase", event.Phase, "origin", event.Origin, "failure", event.Failure)
46 if event.Phase == "completed" {
47 a.requestHistoricalLegacyReconciliation(ctx)
48 }
49 },
50 OnRevision: func(revision uint64, roots []string, reason string) {
51 a.emitProjectTreeChangedV2(revision, roots, reason)
52 },
53 })
54 if err != nil {
55 if deferredIntegrity && projectiondb.IsCorruptionError(err) {
56 deferredIntegrity = false
57 continue
58 }
59 slog.Warn("desktop: open session catalog", "err", err)
60 return
61 }
62 // Pair publication with stopSessionCatalog's lifecycle lock. A stopped
63 // owner cannot publish a replacement after shutdown removed its pointer.
64 a.catalogLifecycleMu.Lock()
65 stopped := ctx.Err() != nil || a.shuttingDown.Load()
66 if !stopped {
67 a.sessionCatalog.Store(catalog)
68 }
69 a.catalogLifecycleMu.Unlock()
70 if stopped {
71 _ = catalog.Close(context.Background())
72 return
73 }
74 if freshGeneration {
75 catalog.MarkRepairReason("generation_upgrade")
76 }
77 a.watchSessionCatalog(ctx, catalog, metadataRequests, func() {
78 if !initialReconcileFinished {
79 close(initialReconcileDone)
80 initialReconcileFinished = true
81 }
82 })
83 select {
84 case <-catalog.Invalidated():
85 // Invalidate the published owner before closing every read lease.
86 // Only then may the normal validating open quarantine the database.
87 a.sessionCatalog.CompareAndSwap(catalog, nil)
88 if err := catalog.Close(context.Background()); err != nil {
89 slog.Warn("desktop: close invalid catalog", "err", err)
90 return
91 }
92 revisionFloor = catalog.Status().Revision + 1
93 deferredIntegrity, freshGeneration = false, true
94 default:
95 return
96 }
97 }
98 }
99
99 lines GO