git-codereview: avoid race in loadGerritOrigin
Use a mutex in loadGerritOrigin to avoid race when called in parallel
by "git coderevew pending". Add a new initialized field so that the
code knows when auth has been initialized. Adjust tests accordingly.
The test is simply "go test -race".
Fixes golang/go#43670
Change-Id: Ifb060fca6ed463f1d11a2959d03fca5e14e238c6
Reviewed-on: https://go-review.googlesource.com/c/review/+/287012
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/git-codereview/api.go b/git-codereview/api.go
index c7126f9..afd421f 100644
--- a/git-codereview/api.go
+++ b/git-codereview/api.go
@@ -17,10 +17,13 @@
"runtime"
"sort"
"strings"
+ "sync"
)
// auth holds cached data about authentication to Gerrit.
var auth struct {
+ initialized bool
+
host string // "go.googlesource.com"
url string // "https://go-review.googlesource.com"
project string // "go", "tools", "crypto", etc
@@ -34,12 +37,21 @@
password string
}
+// loadGerritOriginMutex is used to control access when initializing auth
+// in loadGerritOrigin, which can be called in parallel by "pending".
+// We use a mutex rather than a sync.Once because the tests clear auth.
+var loadGerritOriginMutex sync.Mutex
+
// loadGerritOrigin loads the Gerrit host name from the origin remote.
+// This sets auth.{initialized,host,url,project}.
// If the origin remote does not appear to be a Gerrit server
// (is missing, is GitHub, is not https, has too many path elements),
// loadGerritOrigin dies.
func loadGerritOrigin() {
- if auth.host != "" {
+ loadGerritOriginMutex.Lock()
+ defer loadGerritOriginMutex.Unlock()
+
+ if auth.initialized {
return
}
@@ -52,6 +64,8 @@
if err != nil {
dief("failed to load Gerrit origin: %v", err)
}
+
+ auth.initialized = true
}
// loadGerritOriginInternal does the work of loadGerritOrigin, just extracted out
@@ -279,7 +293,7 @@
}
// fullChangeID returns the unambigous Gerrit change ID for the commit c on branch b.
-// The retruned ID has the form project~originbranch~Ihexhexhexhexhex.
+// The returned ID has the form project~originbranch~Ihexhexhexhexhex.
// See https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-id for details.
func fullChangeID(b *Branch, c *Commit) string {
loadGerritOrigin()
diff --git a/git-codereview/api_test.go b/git-codereview/api_test.go
index 28ccce9..8494e90 100644
--- a/git-codereview/api_test.go
+++ b/git-codereview/api_test.go
@@ -94,6 +94,7 @@
for i, tt := range authTests {
t.Logf("#%d", i)
+ auth.initialized = false
auth.user = ""
auth.password = ""
auth.cookieName = ""
@@ -175,6 +176,7 @@
}
for _, item := range list {
+ auth.initialized = false
auth.host = ""
auth.url = ""
auth.project = ""
diff --git a/git-codereview/mail_test.go b/git-codereview/mail_test.go
index 93b89f4..72fea82 100644
--- a/git-codereview/mail_test.go
+++ b/git-codereview/mail_test.go
@@ -17,9 +17,11 @@
h := CurrentBranch().Pending()[0].ShortHash
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
@@ -59,9 +61,11 @@
func TestDoNotMailTempFiles(t *testing.T) {
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
@@ -95,9 +99,11 @@
gt.work(t)
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
@@ -182,9 +188,11 @@
defer gt.done()
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
@@ -241,9 +249,11 @@
h := CurrentBranch().Pending()[0].ShortHash
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
@@ -265,9 +275,11 @@
h := CurrentBranch().Pending()[0].ShortHash
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
@@ -290,9 +302,11 @@
defer gt.done()
// fake auth information to avoid Gerrit error
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
diff --git a/git-codereview/pending_test.go b/git-codereview/pending_test.go
index 6899213..35e51ac 100644
--- a/git-codereview/pending_test.go
+++ b/git-codereview/pending_test.go
@@ -476,10 +476,12 @@
func testPendingArgs(t *testing.T, args []string, want string) {
setHelper(t)
// fake auth information to avoid Gerrit error
- if auth.host == "" {
+ if !auth.initialized {
+ auth.initialized = true
auth.host = "gerrit.fake"
auth.user = "not-a-user"
defer func() {
+ auth.initialized = false
auth.host = ""
auth.user = ""
}()
diff --git a/git-codereview/util_test.go b/git-codereview/util_test.go
index 775b244..fd419a2 100644
--- a/git-codereview/util_test.go
+++ b/git-codereview/util_test.go
@@ -437,6 +437,7 @@
t.Fatalf("starting fake gerrit: %v", err)
}
+ auth.initialized = true
auth.host = l.Addr().String()
auth.url = "http://" + auth.host
auth.project = "proj"
@@ -450,6 +451,7 @@
func (s *gerritServer) done() {
s.l.Close()
+ auth.initialized = false
auth.host = ""
auth.url = ""
auth.project = ""