1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17
18 package gc
19
20 import (
21 "context"
22 "errors"
23 "fmt"
24
25 "go4.org/syncutil"
26 )
27
28 const buffered = 32
29
30
31 type Item interface{}
32
33
34 type Collector struct {
35
36
37 World World
38
39 Marker Marker
40 Roots Enumerator
41 Sweeper Enumerator
42 ItemEnumerator ItemEnumerator
43 Deleter Deleter
44 }
45
46 type Marker interface {
47
48
49 Mark(Item) error
50
51
52
53 IsMarked(Item) (bool, error)
54 }
55
56
57 type World interface {
58 Stop() error
59 Start() error
60 }
61
62 type Deleter interface {
63
64
65
66 Delete(Item) error
67 }
68
69
70 type Enumerator interface {
71
72
73
74
75
76
77 Enumerate(context.Context, chan<- Item) error
78 }
79
80
81 type ItemEnumerator interface {
82
83
84 EnumerateItem(context.Context, Item, chan<- Item) error
85 }
86
87
88 func (c *Collector) markItem(ctx context.Context, it Item, isRoot bool) error {
89 if !isRoot {
90 marked, err := c.Marker.IsMarked(it)
91 if err != nil {
92 return err
93 }
94 if marked {
95 return nil
96 }
97 }
98 if err := c.Marker.Mark(it); err != nil {
99 return err
100 }
101
102
103 ctx, cancel := context.WithCancel(ctx)
104 defer cancel()
105
106 ch := make(chan Item, buffered)
107 var grp syncutil.Group
108 grp.Go(func() error {
109 return c.ItemEnumerator.EnumerateItem(ctx, it, ch)
110 })
111 grp.Go(func() error {
112 for it := range ch {
113 if err := c.markItem(ctx, it, false); err != nil {
114 return err
115 }
116 }
117 return nil
118 })
119 return grp.Err()
120 }
121
122
123 func (c *Collector) Collect(ctx context.Context) (err error) {
124 if c.World == nil {
125 return errors.New("no World")
126 }
127 if c.Marker == nil {
128 return errors.New("no Marker")
129 }
130 if c.Roots == nil {
131 return errors.New("no Roots")
132 }
133 if c.Sweeper == nil {
134 return errors.New("no Sweeper")
135 }
136 if c.ItemEnumerator == nil {
137 return errors.New("no ItemEnumerator")
138 }
139 if c.Deleter == nil {
140 return errors.New("no Deleter")
141 }
142 if err := c.World.Stop(); err != nil {
143 return err
144 }
145 defer func() {
146 startErr := c.World.Start()
147 if err == nil {
148 err = startErr
149 }
150 }()
151
152
153 roots := make(chan Item, buffered)
154 markCtx, cancelMark := context.WithCancel(ctx)
155 var marker syncutil.Group
156 marker.Go(func() error {
157 defer cancelMark()
158 for it := range roots {
159 if err := c.markItem(markCtx, it, true); err != nil {
160 return err
161 }
162 }
163 return nil
164 })
165 marker.Go(func() error {
166 return c.Roots.Enumerate(markCtx, roots)
167 })
168 if err := marker.Err(); err != nil {
169 return fmt.Errorf("Mark failure: %v", err)
170 }
171
172
173 all := make(chan Item, buffered)
174 sweepCtx, cancelSweep := context.WithCancel(ctx)
175 var sweeper syncutil.Group
176 sweeper.Go(func() error {
177 return c.Sweeper.Enumerate(sweepCtx, all)
178 })
179 sweeper.Go(func() error {
180 defer cancelSweep()
181 for it := range all {
182 ok, err := c.Marker.IsMarked(it)
183 if err != nil {
184 return err
185 }
186 if !ok {
187 if err := c.Deleter.Delete(it); err != nil {
188 return err
189 }
190 }
191 }
192 return nil
193 })
194 if err := sweeper.Err(); err != nil {
195 return fmt.Errorf("Sweep failure: %v", err)
196 }
197 return nil
198 }