返回 DeepSeek-Reasonix
manual_session_recovery.go
根目录 / desktop / manual_session_recovery.go
1 package main
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "time"
8
9 "reasonix/internal/session"
10 )
11
12 // Selecting a persisted creation's own tab must not build another controller
13 // or wait for the background builder. Composer storage is available already;
14 // submission stays gated by readiness and completion never changes selection.
15 func (a *App) openManualCreationSurface(ref session.SessionRef, navigationSequence uint64) (bool, error) {
16 defer a.lockRuntimeMutation("open manual creation surface")()
17 a.mu.Lock()
18 defer a.mu.Unlock()
19 if a.desktopSessions.navigationSeq.Load() != navigationSequence {
20 return false, errSessionNavigationSuperseded
21 }
22 for _, tab := range a.tabs {
23 if !tab.removed && tab.SessionID == ref.SessionID && tab.PendingCreateOperationID != "" {
24 a.activeTabID = tab.ID
25 a.saveTabsLocked()
26 return true, nil
27 }
28 }
29 return false, nil
30 }
31
32 func (a *App) startManualSessionTab(ctx context.Context, tab *WorkspaceTab, report func(string)) error {
33 // Build registration and shutdown admission share the same boundary.
34 a.manualCreationMu.Lock()
35 if a.shuttingDown.Load() || ctx.Err() != nil {
36 a.manualCreationMu.Unlock()
37 return context.Canceled
38 }
39 a.mu.RLock()
40 ready, build := tab.Ctrl != nil, tab.buildExecution
41 a.mu.RUnlock()
42 if !ready && build == nil {
43 a.startTabControllerBuildMode(tab, true)
44 }
45 a.manualCreationMu.Unlock()
46 for {
47 a.mu.RLock()
48 builds := make([]*tabBuildExecution, 0, len(tab.buildExecutions))
49 for execution := range tab.buildExecutions {
50 builds = append(builds, execution)
51 }
52 a.mu.RUnlock()
53 if len(builds) == 0 {
54 break
55 }
56 for _, execution := range builds {
57 select {
58 case <-execution.done:
59 case <-ctx.Done():
60 report("stopping")
61 for _, pending := range builds {
62 pending.cancel()
63 }
64 <-execution.done // Only actual execution exit closes this channel.
65 }
66 }
67 }
68 if err := ctx.Err(); err != nil {
69 return err
70 }
71 a.mu.RLock()
72 defer a.mu.RUnlock()
73 if tab.Ctrl == nil || tab.removed {
74 return errors.New("session runtime did not start: " + tab.StartupErr)
75 }
76 return nil
77 }
78
79 func (a *App) ListManualSessionCreations() (views []ManualSessionCreationView, err error) {
80 defer func() { err = sessionUIError(err, "", "") }()
81 rows, err := a.sessionUIStore().List(a.bootContext(), "creation")
82 views = []ManualSessionCreationView{}
83 if err != nil {
84 return views, err
85 }
86 for _, row := range rows {
87 var view ManualSessionCreationView
88 if err := json.Unmarshal(row.Payload, &view); err != nil {
89 return views, err
90 }
91 if view.Phase != "ready" {
92 views = append(views, a.creationView(view))
93 }
94 }
95 return views, nil
96 }
97
98 func (a *App) reconcileManualSessionCreations() {
99 m := a.creationManager()
100 select {
101 case <-a.tabsRestoredSignal():
102 m.armRecovery()
103 case <-m.ctx.Done():
104 }
105 }
106
107 func (a *App) stopManualCreations() error {
108 a.manualCreationMu.Lock()
109 m := a.manualCreations
110 a.manualCreationMu.Unlock()
111 if m == nil {
112 return nil
113 }
114 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
115 defer cancel()
116 return m.CancelAndWait(ctx)
117 }
118
118 lines GO