func doTreeWalkP(v types.Value, vr types.ValueReader, cb SomeCallback, concurrency int) { rq := newRefQueue() f := newFailure() visited := map[hash.Hash]bool{} mu := sync.Mutex{} wg := sync.WaitGroup{} var processVal func(v types.Value, r *types.Ref) processVal = func(v types.Value, r *types.Ref) { if cb(v, r) { return } if sr, ok := v.(types.Ref); ok { wg.Add(1) rq.tail() <- sr } else { switch coll := v.(type) { case types.List: coll.IterAll(func(c types.Value, index uint64) { processVal(c, nil) }) case types.Set: coll.IterAll(func(c types.Value) { processVal(c, nil) }) case types.Map: coll.IterAll(func(k, c types.Value) { processVal(k, nil) processVal(c, nil) }) default: for _, c := range v.ChildValues() { processVal(c, nil) } } } } processRef := func(r types.Ref) { defer wg.Done() mu.Lock() skip := visited[r.TargetHash()] visited[r.TargetHash()] = true mu.Unlock() if skip || f.didFail() { return } target := r.TargetHash() v := vr.ReadValue(target) if v == nil { f.fail(fmt.Errorf("Attempt to copy absent ref:%s", target.String())) return } processVal(v, &r) } iter := func() { for r := range rq.head() { processRef(r) } } for i := 0; i < concurrency; i++ { go iter() } processVal(v, nil) wg.Wait() rq.close() f.checkNotFailed() }