返回 DeepSeek-Reasonix
barrier.go
根目录 / internal / checkpoint / barrier.go
1 package checkpoint
2
3 import (
4 "fmt"
5 "sync"
6 "sync/atomic"
7 )
8
9 // MutationBarrier provides exclusive workspace mutation access for rewind
10 // transactions. It is intentionally separate from App.mu / Controller locks so
11 // file I/O never runs under those mutexes.
12 //
13 // Writers call EnterWrite / ExitWrite around mutations.
14 // Rewind holds EnterExclusive for the whole prepare+commit critical section.
15 type MutationBarrier struct {
16 mu sync.Mutex
17 cond *sync.Cond
18 writers int
19 exclusive bool
20 // generation increments on every exclusive release so prepare tokens can
21 // detect concurrent mutation without relying on wall-clock time.
22 generation atomic.Uint64
23 // closed rejects new enters after shutdown (optional).
24 closed bool
25 }
26
27 // NewMutationBarrier returns a ready barrier.
28 func NewMutationBarrier() *MutationBarrier {
29 b := &MutationBarrier{}
30 b.cond = sync.NewCond(&b.mu)
31 return b
32 }
33
34 // Generation returns the current exclusive-release generation.
35 func (b *MutationBarrier) Generation() uint64 {
36 if b == nil {
37 return 0
38 }
39 return b.generation.Load()
40 }
41
42 // EnterWrite blocks until exclusive access is free, then increments the writer count.
43 func (b *MutationBarrier) EnterWrite() error {
44 if b == nil {
45 return nil
46 }
47 b.mu.Lock()
48 defer b.mu.Unlock()
49 for b.exclusive || b.closed {
50 if b.closed {
51 return fmt.Errorf("mutation barrier closed")
52 }
53 b.cond.Wait()
54 }
55 b.writers++
56 return nil
57 }
58
59 // TryEnterWrite is a non-blocking EnterWrite.
60 func (b *MutationBarrier) TryEnterWrite() bool {
61 if b == nil {
62 return true
63 }
64 b.mu.Lock()
65 defer b.mu.Unlock()
66 if b.exclusive || b.closed {
67 return false
68 }
69 b.writers++
70 return true
71 }
72
73 // ExitWrite decrements the writer count and advances the workspace generation.
74 // Plans prepared before a completed writer can therefore never authorize a
75 // later commit without a fresh preview.
76 func (b *MutationBarrier) ExitWrite() {
77 if b == nil {
78 return
79 }
80 b.mu.Lock()
81 defer b.mu.Unlock()
82 if b.writers > 0 {
83 b.writers--
84 b.generation.Add(1)
85 }
86 if b.writers == 0 {
87 b.cond.Broadcast()
88 }
89 }
90
91 // EnterExclusive waits until no writers hold the barrier, then takes exclusive.
92 func (b *MutationBarrier) EnterExclusive() error {
93 if b == nil {
94 return nil
95 }
96 b.mu.Lock()
97 defer b.mu.Unlock()
98 for b.exclusive || b.writers > 0 || b.closed {
99 if b.closed {
100 return fmt.Errorf("mutation barrier closed")
101 }
102 b.cond.Wait()
103 }
104 b.exclusive = true
105 return nil
106 }
107
108 // TryEnterExclusive is a non-blocking EnterExclusive.
109 func (b *MutationBarrier) TryEnterExclusive() bool {
110 if b == nil {
111 return true
112 }
113 b.mu.Lock()
114 defer b.mu.Unlock()
115 if b.exclusive || b.writers > 0 || b.closed {
116 return false
117 }
118 b.exclusive = true
119 return true
120 }
121
122 // ExitExclusive releases exclusive access and bumps generation.
123 func (b *MutationBarrier) ExitExclusive() {
124 if b == nil {
125 return
126 }
127 b.mu.Lock()
128 defer b.mu.Unlock()
129 b.exclusive = false
130 b.generation.Add(1)
131 b.cond.Broadcast()
132 }
133
134 // Busy reports whether exclusive is held or writers are active.
135 func (b *MutationBarrier) Busy() bool {
136 if b == nil {
137 return false
138 }
139 b.mu.Lock()
140 defer b.mu.Unlock()
141 return b.exclusive || b.writers > 0
142 }
143
144 // Close rejects future enters (best-effort shutdown).
145 func (b *MutationBarrier) Close() {
146 if b == nil {
147 return
148 }
149 b.mu.Lock()
150 b.closed = true
151 b.cond.Broadcast()
152 b.mu.Unlock()
153 }
154
154 lines GO