// 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 // Tricolor implements basic, single-thread tri-color GC. Given the roots, the // complete set and a refs function, this returns the unreachable 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 []string, all []string, refs func(ref string) []string) []string { var ( grays []string // maintain a gray "stack" seen = map[string]struct{}{} // or not "white", basically "seen" reachable = map[string]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 // mark all the referenced objects as gray for _, target := range refs(id) { if _, ok := seen[target]; !ok { grays = append(grays, target) } } // mark as black when done reachable[id] = struct{}{} } // All black objects are now reachable, and all white objects are // unreachable. Free those that are white! var whites []string for _, obj := range all { if _, ok := reachable[obj]; !ok { whites = append(whites, obj) } } return whites }