返回 DeepSeek-Reasonix
background_scope.go
根目录 / internal / control / background_scope.go
1 package control
2
3 import (
4 "reasonix/internal/event"
5 "reasonix/internal/jobs"
6 )
7
8 type controllerBackground struct {
9 scope *jobs.SessionBackgroundScope
10 recorder jobs.TaskRecorder
11 sink event.Sink
12 release func()
13 rollback func()
14 publish func()
15 candidate bool
16 retired bool
17 }
18
19 func (c *Controller) BackgroundScope() *jobs.SessionBackgroundScope { return c.background.scope }
20
21 // ModelReplacementBlocked retains the strict foreground/interaction guards,
22 // but session-owned processes need not stop for a model-only replacement.
23 func ModelReplacementBlocked(ctrl interface{ RuntimeStatus() RuntimeStatus }) bool {
24 if ctrl == nil {
25 return false
26 }
27 if c, ok := ctrl.(*Controller); ok && c.background.scope != nil {
28 c.mu.Lock()
29 busy := c.closed || c.bodyActiveLocked() || c.cancelRequestedLocked() || c.maintenance != nil || c.finalizingLocked() || c.recoveryRequiredLocked()
30 c.mu.Unlock()
31 return busy || c.PendingPrompt() || len(c.jobs.BlockingJobs(c.parentSessionID())) > 0
32 }
33 status := ctrl.RuntimeStatus()
34 return status.Running || status.PendingPrompt || status.BackgroundJobs > 0
35 }
36
37 func (c *Controller) ModelReplacementJobs() []jobs.View {
38 if c.background.scope == nil {
39 return c.Jobs()
40 }
41 return c.jobs.BlockingJobs(c.parentSessionID())
42 }
43
44 // StageBackgroundReplacement transfers the reservation to the candidate. It
45 // remains sealed until publication or candidate disposal, not merely Build.
46 func (c *Controller) StageBackgroundReplacement(release func()) {
47 c.mu.Lock()
48 c.background.release = release
49 c.mu.Unlock()
50 }
51
52 func (c *Controller) finishBackgroundReplacement(publish bool) {
53 c.mu.Lock()
54 release := c.background.release
55 rollback := c.background.rollback
56 publishRuntime := c.background.publish
57 c.background.release = nil
58 c.background.rollback = nil
59 c.background.publish = nil
60 c.mu.Unlock()
61 if publish && publishRuntime != nil {
62 publishRuntime()
63 }
64 if publish && c.background.scope != nil {
65 c.background.scope.Bind(c.background.sink, c.background.recorder)
66 c.mu.Lock()
67 c.background.candidate = false
68 c.mu.Unlock()
69 }
70 if !publish && rollback != nil {
71 rollback()
72 }
73 if release != nil {
74 release()
75 }
76 }
77
78 func (c *Controller) PublishBackgroundScope() { c.finishBackgroundReplacement(true) }
79
80 func (c *Controller) StageReplacementRollback(rollback func()) {
81 c.mu.Lock()
82 c.background.rollback = rollback
83 c.mu.Unlock()
84 }
85
86 // StageReplacementPublication commits prepared, nonblocking runtime metadata
87 // only after execution ownership transfers. It must not perform I/O or invoke
88 // external callbacks while the host's compare-and-publish lock is held.
89 func (c *Controller) StageReplacementPublication(publish func()) {
90 c.mu.Lock()
91 c.background.publish = publish
92 c.mu.Unlock()
93 }
94
95 func (c *Controller) isBackgroundCandidate() bool {
96 c.mu.Lock()
97 defer c.mu.Unlock()
98 return c.background.candidate
99 }
100
101 func (c *Controller) receivesBackgroundRuntimeEvents() bool {
102 c.mu.Lock()
103 inactive := c.background.candidate || c.background.retired || c.closed
104 c.mu.Unlock()
105 return !inactive && (c.background.scope == nil || !c.background.scope.Manager.ReplacementInProgress())
106 }
107
107 lines GO