Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit eac1f47

Browse files
authored
Merge pull request #1155 from tamird/simplify-errors
gps: use golang.org/x/sync/errgroup
2 parents 35e1a38 + 01209b5 commit eac1f47

32 files changed

+1785
-106
lines changed

Gopkg.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/gps/manager_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ func TestMgrMethodsFailWithBadPath(t *testing.T) {
343343
if _, _, err = sm.GetManifestAndLock(bad, nil, naiveAnalyzer{}); err == nil {
344344
t.Error("GetManifestAndLock() did not error on bad input")
345345
}
346-
if err = sm.ExportProject(bad, nil, ""); err == nil {
346+
if err = sm.ExportProject(context.Background(), bad, nil, ""); err == nil {
347347
t.Error("ExportProject() did not error on bad input")
348348
}
349349
}
@@ -758,7 +758,7 @@ func TestErrAfterRelease(t *testing.T) {
758758
t.Errorf("GetManifestAndLock errored after Release(), but with unexpected error: %T %s", terr, terr.Error())
759759
}
760760

761-
err = sm.ExportProject(id, nil, "")
761+
err = sm.ExportProject(context.Background(), id, nil, "")
762762
if err == nil {
763763
t.Errorf("ExportProject did not error after calling Release()")
764764
} else if terr, ok := err.(smIsReleased); !ok {

internal/gps/maybe_source.go

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,29 @@ type maybeSource interface {
2828
getURL() string
2929
}
3030

31+
type errorSlice []error
32+
33+
func (errs *errorSlice) Error() string {
34+
var buf bytes.Buffer
35+
for _, err := range *errs {
36+
fmt.Fprintf(&buf, "\n\t%s", err)
37+
}
38+
return buf.String()
39+
}
40+
3141
type maybeSources []maybeSource
3242

3343
func (mbs maybeSources) try(ctx context.Context, cachedir string, c singleSourceCache, superv *supervisor) (source, sourceState, error) {
34-
var e sourceFailures
44+
var errs errorSlice
3545
for _, mb := range mbs {
3646
src, state, err := mb.try(ctx, cachedir, c, superv)
3747
if err == nil {
3848
return src, state, nil
3949
}
40-
e = append(e, sourceSetupFailure{
41-
ident: mb.getURL(),
42-
err: err,
43-
})
50+
errs = append(errs, errors.Wrapf(err, "failed to set up %q", mb.getURL()))
4451
}
45-
return nil, 0, e
52+
53+
return nil, 0, errors.Wrap(&errs, "no valid source could be created")
4654
}
4755

4856
// This really isn't generally intended to be used - the interface is for
@@ -56,27 +64,6 @@ func (mbs maybeSources) getURL() string {
5664
return strings.Join(strslice, "\n")
5765
}
5866

59-
type sourceSetupFailure struct {
60-
ident string
61-
err error
62-
}
63-
64-
func (e sourceSetupFailure) Error() string {
65-
return fmt.Sprintf("failed to set up %q, error %s", e.ident, e.err.Error())
66-
}
67-
68-
type sourceFailures []sourceSetupFailure
69-
70-
func (sf sourceFailures) Error() string {
71-
var buf bytes.Buffer
72-
fmt.Fprintf(&buf, "no valid source could be created:")
73-
for _, e := range sf {
74-
fmt.Fprintf(&buf, "\n\t%s", e.Error())
75-
}
76-
77-
return buf.String()
78-
}
79-
8067
// sourceCachePath returns a url-sanitized source cache dir path.
8168
func sourceCachePath(cacheDir, sourceURL string) string {
8269
return filepath.Join(cacheDir, "sources", sanitizer.Replace(sourceURL))

internal/gps/result.go

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
package gps
66

77
import (
8+
"context"
89
"fmt"
910
"log"
1011
"os"
1112
"path/filepath"
1213
"sync"
1314

1415
"github.com/pkg/errors"
16+
"golang.org/x/sync/errgroup"
1517
)
1618

1719
// A Solution is returned by a solver run. It is mostly just a Lock, with some
@@ -62,103 +64,76 @@ func WriteDepTree(basedir string, l Lock, sm SourceManager, sv bool, logger *log
6264
return fmt.Errorf("must provide non-nil Lock to WriteDepTree")
6365
}
6466

65-
err := os.MkdirAll(basedir, 0777)
66-
if err != nil {
67+
if err := os.MkdirAll(basedir, 0777); err != nil {
6768
return err
6869
}
6970

71+
g, ctx := errgroup.WithContext(context.TODO())
7072
lps := l.Projects()
71-
72-
type resp struct {
73-
i int
74-
err error
73+
sem := make(chan struct{}, concurrentWriters)
74+
var cnt struct {
75+
sync.Mutex
76+
i int
7577
}
76-
respCh := make(chan resp, len(lps))
77-
writeCh := make(chan int, len(lps))
78-
cancel := make(chan struct{})
7978

80-
// Queue work.
8179
for i := range lps {
82-
writeCh <- i
83-
}
84-
close(writeCh)
85-
// Launch writers.
86-
writers := concurrentWriters
87-
if len(lps) < writers {
88-
writers = len(lps)
89-
}
90-
var wg sync.WaitGroup
91-
wg.Add(writers)
92-
for i := 0; i < writers; i++ {
93-
go func() {
94-
defer wg.Done()
80+
p := lps[i] // per-iteration copy
9581

96-
for i := range writeCh {
82+
g.Go(func() error {
83+
err := func() error {
9784
select {
98-
case <-cancel:
99-
return
100-
default:
85+
case sem <- struct{}{}:
86+
defer func() { <-sem }()
87+
case <-ctx.Done():
88+
return ctx.Err()
10189
}
10290

103-
p := lps[i]
104-
to := filepath.FromSlash(filepath.Join(basedir, string(p.Ident().ProjectRoot)))
91+
ident := p.Ident()
92+
projectRoot := string(ident.ProjectRoot)
93+
to := filepath.FromSlash(filepath.Join(basedir, projectRoot))
10594

106-
if err := sm.ExportProject(p.Ident(), p.Version(), to); err != nil {
107-
respCh <- resp{i, errors.Wrapf(err, "failed to export %s", p.Ident().ProjectRoot)}
108-
continue
95+
if err := sm.ExportProject(ctx, ident, p.Version(), to); err != nil {
96+
return errors.Wrapf(err, "failed to export %s", projectRoot)
10997
}
11098

11199
if sv {
112-
select {
113-
case <-cancel:
114-
return
115-
default:
100+
if err := ctx.Err(); err != nil {
101+
return err
116102
}
117103

118104
if err := filepath.Walk(to, stripVendor); err != nil {
119-
respCh <- resp{i, errors.Wrapf(err, "failed to strip vendor from %s", p.Ident().ProjectRoot)}
120-
continue
105+
return errors.Wrapf(err, "failed to strip vendor from %s", projectRoot)
121106
}
122107
}
123108

124-
respCh <- resp{i, nil}
125-
}
126-
}()
127-
}
128-
// Monitor writers
129-
go func() {
130-
wg.Wait()
131-
close(respCh)
132-
}()
133-
134-
// Log results and collect errors
135-
var errs []error
136-
var cnt int
137-
for resp := range respCh {
138-
cnt++
139-
msg := "Wrote"
140-
if resp.err != nil {
141-
if len(errs) == 0 {
142-
close(cancel)
109+
return nil
110+
}()
111+
112+
switch err {
113+
case context.Canceled, context.DeadlineExceeded:
114+
// Don't log "secondary" errors.
115+
default:
116+
msg := "Wrote"
117+
if err != nil {
118+
msg = "Failed to write"
119+
}
120+
121+
// Log and increment atomically to prevent re-ordering.
122+
cnt.Lock()
123+
cnt.i++
124+
logger.Printf("(%d/%d) %s %s@%s\n", cnt.i, len(lps), msg, p.Ident(), p.Version())
125+
cnt.Unlock()
143126
}
144-
errs = append(errs, resp.err)
145-
msg = "Failed to write"
146-
}
147-
p := lps[resp.i]
148-
logger.Printf("(%d/%d) %s %s@%s\n", cnt, len(lps), msg, p.Ident(), p.Version())
149-
}
150127

151-
if len(errs) > 0 {
152-
logger.Println("Failed to write dep tree. The following errors occurred:")
153-
for i, err := range errs {
154-
logger.Printf("(%d/%d) %s\n", i+1, len(errs), err)
155-
}
128+
return err
129+
})
130+
}
156131

132+
err := g.Wait()
133+
if err != nil {
157134
os.RemoveAll(basedir)
158-
159-
return errors.New("failed to write dep tree")
160135
}
161-
return nil
136+
return errors.Wrap(err, "failed to write dep tree")
162137
}
163138

164139
func (r solution) Projects() []LockedProject {

internal/gps/solve_basic_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package gps
66

77
import (
8+
"context"
89
"fmt"
910
"regexp"
1011
"strings"
@@ -1460,7 +1461,7 @@ func (sm *depspecSourceManager) SyncSourceFor(id ProjectIdentifier) error {
14601461

14611462
func (sm *depspecSourceManager) Release() {}
14621463

1463-
func (sm *depspecSourceManager) ExportProject(id ProjectIdentifier, v Version, to string) error {
1464+
func (sm *depspecSourceManager) ExportProject(context.Context, ProjectIdentifier, Version, string) error {
14641465
return fmt.Errorf("dummy sm doesn't support exporting")
14651466
}
14661467

internal/gps/source_manager.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ type SourceManager interface {
6565

6666
// ExportProject writes out the tree of the provided import path, at the
6767
// provided version, to the provided directory.
68-
ExportProject(ProjectIdentifier, Version, string) error
68+
ExportProject(context.Context, ProjectIdentifier, Version, string) error
6969

7070
// DeduceRootProject takes an import path and deduces the corresponding
7171
// project/source root.
@@ -460,17 +460,17 @@ func (sm *SourceMgr) SyncSourceFor(id ProjectIdentifier) error {
460460

461461
// ExportProject writes out the tree of the provided ProjectIdentifier's
462462
// ProjectRoot, at the provided version, to the provided directory.
463-
func (sm *SourceMgr) ExportProject(id ProjectIdentifier, v Version, to string) error {
463+
func (sm *SourceMgr) ExportProject(ctx context.Context, id ProjectIdentifier, v Version, to string) error {
464464
if atomic.LoadInt32(&sm.releasing) == 1 {
465465
return smIsReleased{}
466466
}
467467

468-
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
468+
srcg, err := sm.srcCoord.getSourceGatewayFor(ctx, id)
469469
if err != nil {
470470
return err
471471
}
472472

473-
return srcg.exportVersionTo(context.TODO(), v, to)
473+
return srcg.exportVersionTo(ctx, v, to)
474474
}
475475

476476
// DeduceProjectRoot takes an import path and deduces the corresponding

vendor/golang.org/x/net/.gitattributes

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/.gitignore

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/AUTHORS

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/CONTRIBUTING.md

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/CONTRIBUTORS

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)