github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/gc/gc.go (about) 1 /* 2 Copyright 2014 The Camlistore Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package gc defines a generic garbage collector. 18 package gc 19 20 import ( 21 "errors" 22 "fmt" 23 24 "camlistore.org/pkg/context" 25 "camlistore.org/pkg/syncutil" 26 ) 27 28 const buffered = 32 // arbitrary 29 30 // Item is something that exists that may or may not survive a GC collection. 31 type Item interface{} 32 33 // A Collector performs a garbage collection. 34 type Collector struct { 35 // World specifies a World that should be stopped before a 36 // collection and started again after. 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 // Mark marks that an item should exist. 48 // It must be safe for calls from concurrent goroutines. 49 Mark(Item) error 50 51 // IsMarked returns whether the item is marked. 52 // It must be safe for calls from concurrent goroutines. 53 IsMarked(Item) (bool, error) 54 } 55 56 // World defines the thing that should be stopped before GC and started after. 57 type World interface { 58 Stop() error 59 Start() error 60 } 61 62 type Deleter interface { 63 // Delete deletes an item that was deemed unreachable via 64 // the garbage collector. 65 // It must be safe for calls from concurrent goroutines. 66 Delete(Item) error 67 } 68 69 // Enumerator enumerates items. 70 type Enumerator interface { 71 // Enumerate enumerates items (which items depends on usage) 72 // and sends them to the provided channel. Regardless of return 73 // value, the channel should be closed. 74 // 75 // If the provided context is closed, Enumerate should return 76 // with an error (typically context.ErrCanceled) 77 Enumerate(*context.Context, chan<- Item) error 78 } 79 80 // ItemEnumerator enumerates all the edges out from an item. 81 type ItemEnumerator interface { 82 // EnumerateItme is like Enuerator's Enumerate, but specific 83 // to the provided item. 84 EnumerateItem(*context.Context, Item, chan<- Item) error 85 } 86 87 // ctx will be canceled on failure 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 ch := make(chan Item, buffered) 103 var grp syncutil.Group 104 grp.Go(func() error { 105 return c.ItemEnumerator.EnumerateItem(ctx, it, ch) 106 }) 107 grp.Go(func() error { 108 for it := range ch { 109 if err := c.markItem(ctx, it, false); err != nil { 110 return err 111 } 112 } 113 return nil 114 }) 115 if err := grp.Err(); err != nil { 116 ctx.Cancel() 117 return err 118 } 119 return nil 120 } 121 122 // Collect performs a garbage collection. 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 // Mark. 153 roots := make(chan Item, buffered) 154 markCtx := ctx.New() 155 var marker syncutil.Group 156 marker.Go(func() error { 157 defer markCtx.Cancel() 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 // Sweep. 173 all := make(chan Item, buffered) 174 sweepCtx := ctx.New() 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 sweepCtx.Done() 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 }