
Marks and sweeps unreferenced objects. Add snapshot cleanup to metadata. Add content garbage collection Add dirty flags for snapshotters and content store which are set on deletion and used during the next garbage collection. Cleanup content store backend when content metadata is removed. Signed-off-by: Derek McGowan <derek@mcgstyle.net>
161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
// Package gc experiments with providing central gc tooling to ensure
|
|
// deterministic resource removal within containerd.
|
|
//
|
|
// For now, we just have a single exported implementation that can be used
|
|
// under certain use cases.
|
|
package gc
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
)
|
|
|
|
// Resourcetype represents type of resource at a node
|
|
type ResourceType uint8
|
|
|
|
// Node presents a resource which has a type and key,
|
|
// this node can be used to lookup other nodes.
|
|
type Node struct {
|
|
Type ResourceType
|
|
Namespace string
|
|
Key string
|
|
}
|
|
|
|
// Tricolor implements basic, single-thread tri-color GC. Given the roots, the
|
|
// complete set and a refs function, this function returns a map of all
|
|
// reachable objects.
|
|
//
|
|
// Correct usage requires that the caller not allow the arguments to change
|
|
// until the result is used to delete objects in the system.
|
|
//
|
|
// It will allocate memory proportional to the size of the reachable set.
|
|
//
|
|
// We can probably use this to inform a design for incremental GC by injecting
|
|
// callbacks to the set modification algorithms.
|
|
func Tricolor(roots []Node, refs func(ref Node) ([]Node, error)) (map[Node]struct{}, error) {
|
|
var (
|
|
grays []Node // maintain a gray "stack"
|
|
seen = map[Node]struct{}{} // or not "white", basically "seen"
|
|
reachable = map[Node]struct{}{} // or "block", in tri-color parlance
|
|
)
|
|
|
|
grays = append(grays, roots...)
|
|
|
|
for len(grays) > 0 {
|
|
// Pick any gray object
|
|
id := grays[len(grays)-1] // effectively "depth first" because first element
|
|
grays = grays[:len(grays)-1]
|
|
seen[id] = struct{}{} // post-mark this as not-white
|
|
rs, err := refs(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// mark all the referenced objects as gray
|
|
for _, target := range rs {
|
|
if _, ok := seen[target]; !ok {
|
|
grays = append(grays, target)
|
|
}
|
|
}
|
|
|
|
// mark as black when done
|
|
reachable[id] = struct{}{}
|
|
}
|
|
|
|
return reachable, nil
|
|
}
|
|
|
|
// ConcurrentMark implements simple, concurrent GC. All the roots are scanned
|
|
// and the complete set of references is formed by calling the refs function
|
|
// for each seen object. This function returns a map of all object reachable
|
|
// from a root.
|
|
//
|
|
// Correct usage requires that the caller not allow the arguments to change
|
|
// until the result is used to delete objects in the system.
|
|
//
|
|
// It will allocate memory proportional to the size of the reachable set.
|
|
func ConcurrentMark(ctx context.Context, root <-chan Node, refs func(context.Context, Node, func(Node)) error) (map[Node]struct{}, error) {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
|
|
var (
|
|
grays = make(chan Node)
|
|
seen = map[Node]struct{}{} // or not "white", basically "seen"
|
|
wg sync.WaitGroup
|
|
|
|
errOnce sync.Once
|
|
refErr error
|
|
)
|
|
|
|
go func() {
|
|
for gray := range grays {
|
|
if _, ok := seen[gray]; ok {
|
|
wg.Done()
|
|
continue
|
|
}
|
|
seen[gray] = struct{}{} // post-mark this as non-white
|
|
|
|
go func(gray Node) {
|
|
defer wg.Done()
|
|
|
|
send := func(n Node) {
|
|
wg.Add(1)
|
|
select {
|
|
case grays <- n:
|
|
case <-ctx.Done():
|
|
wg.Done()
|
|
}
|
|
}
|
|
|
|
if err := refs(ctx, gray, send); err != nil {
|
|
errOnce.Do(func() {
|
|
refErr = err
|
|
cancel()
|
|
})
|
|
}
|
|
|
|
}(gray)
|
|
}
|
|
}()
|
|
|
|
for r := range root {
|
|
wg.Add(1)
|
|
select {
|
|
case grays <- r:
|
|
case <-ctx.Done():
|
|
wg.Done()
|
|
}
|
|
|
|
}
|
|
|
|
// Wait for outstanding grays to be processed
|
|
wg.Wait()
|
|
|
|
close(grays)
|
|
|
|
if refErr != nil {
|
|
return nil, refErr
|
|
}
|
|
if cErr := ctx.Err(); cErr != nil {
|
|
return nil, cErr
|
|
}
|
|
|
|
return seen, nil
|
|
}
|
|
|
|
// Sweep removes all nodes returned through the channel which are not in
|
|
// the reachable set by calling the provided remove function.
|
|
func Sweep(reachable map[Node]struct{}, all <-chan Node, remove func(Node) error) error {
|
|
// All black objects are now reachable, and all white objects are
|
|
// unreachable. Free those that are white!
|
|
for node := range all {
|
|
if _, ok := reachable[node]; !ok {
|
|
if err := remove(node); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|