1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
// Package qver keeps track of 9P qid versions.
package qver
// Version is a 9P qid.version.
//
// A Version may have several child Versions, representing a directory
// hierarchy. The root version is incremented whenever any of the
// children's versions change.
//
// Version is safe to share between goroutines.
type Version struct {
req <-chan struct{}
res <-chan message
bump chan<- struct{}
regChild chan<- childReg
kill chan<- struct{}
}
type Bumper interface {
// Bump increments the version.
Bump()
}
type version struct {
req chan<- struct{}
res chan<- message
bump <-chan struct{}
regChild <-chan childReg
kill <-chan struct{}
v uint32
UpdateFunc
state interface{}
children []*child
}
type childReg struct {
child *Version
done chan<- struct{}
}
type child struct {
*Version
last uint32
}
type message struct {
ver uint32
err error
}
func New(opts ...Option) *Version {
o := parse(opts...)
reqc := make(chan struct{})
resc := make(chan message)
bumpc := make(chan struct{})
childc := make(chan childReg)
killc := make(chan struct{})
go (&version{
reqc,
resc,
bumpc,
childc,
killc,
0,
o.UpdateFunc,
o.state,
nil,
}).run()
v := &Version{reqc, resc, bumpc, childc, killc}
if o.parent != nil {
o.parent.register(v)
}
return v
}
func (v *version) run() {
defer close(v.req)
defer close(v.res)
for {
select {
case v.req <- struct{}{}:
err := v.update()
v.res <- message{v.v, err}
case <-v.bump:
v.v++
case creg := <-v.regChild:
cver, _ := creg.child.Get()
v.children = append(v.children, &child{creg.child, cver})
close(creg.done)
case <-v.kill:
return
}
}
}
func (v *version) update() error {
if v.UpdateFunc != nil {
var err error
v.v, v.state, err = v.UpdateFunc(v.v, v.state)
if err != nil {
return err
}
}
var modified bool
for _, c := range v.children {
mod, err := c.update()
if err != nil {
return err
}
modified = modified || mod
}
if modified {
v.v++
}
return nil
}
// Returns true if modified.
func (c *child) update() (bool, error) {
_, ok := <-c.req
if !ok {
// closed
return false, nil
}
msg := <-c.res
if msg.err != nil {
return false, msg.err
}
if msg.ver != c.last {
// modified
c.last = msg.ver
return true, nil
}
// not modified
return false, nil
}
func (v *Version) register(child *Version) {
done := make(chan struct{})
v.regChild <- childReg{child, done}
<-done
}
func (v *Version) Close() {
v.kill <- struct{}{}
close(v.kill)
close(v.bump)
close(v.regChild)
}
// Get returns the current version.
//
// If the Version was created with the Update option, Get calls the
// UpdateFunc and returns the new version.
func (v *Version) Get() (uint32, error) {
<-v.req
msg := <-v.res
return msg.ver, msg.err
}
// Bump increments the version.
func (v *Version) Bump() {
v.bump <- struct{}{}
}
|