Skip to content

Commit 6cb1074

Browse files
randall77gopherbot
authored andcommitted
runtime: print fatal messages without interleaving
Grab the print lock around the set of prints we use to report fatal errors. This ensures that each fatal error gets reported atomically instead of interleaved with other fatal errors. Fixes #69447 Change-Id: Ib3569f0c8210fd7e19a7d8ef4bc114f07469f317 Reviewed-on: https://go-review.googlesource.com/c/go/+/615655 Auto-Submit: Keith Randall <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Reviewed-by: Keith Randall <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent b17a55d commit 6cb1074

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

src/runtime/crash_test.go

+27-3
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ func TestConcurrentMapWrites(t *testing.T) {
621621
}
622622
testenv.MustHaveGoRun(t)
623623
output := runTestProg(t, "testprog", "concurrentMapWrites")
624-
want := "fatal error: concurrent map writes"
624+
want := "fatal error: concurrent map writes\n"
625625
if !strings.HasPrefix(output, want) {
626626
t.Fatalf("output does not start with %q:\n%s", want, output)
627627
}
@@ -632,7 +632,7 @@ func TestConcurrentMapReadWrite(t *testing.T) {
632632
}
633633
testenv.MustHaveGoRun(t)
634634
output := runTestProg(t, "testprog", "concurrentMapReadWrite")
635-
want := "fatal error: concurrent map read and map write"
635+
want := "fatal error: concurrent map read and map write\n"
636636
if !strings.HasPrefix(output, want) {
637637
t.Fatalf("output does not start with %q:\n%s", want, output)
638638
}
@@ -643,12 +643,36 @@ func TestConcurrentMapIterateWrite(t *testing.T) {
643643
}
644644
testenv.MustHaveGoRun(t)
645645
output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
646-
want := "fatal error: concurrent map iteration and map write"
646+
want := "fatal error: concurrent map iteration and map write\n"
647647
if !strings.HasPrefix(output, want) {
648648
t.Fatalf("output does not start with %q:\n%s", want, output)
649649
}
650650
}
651651

652+
func TestConcurrentMapWritesIssue69447(t *testing.T) {
653+
testenv.MustHaveGoRun(t)
654+
exe, err := buildTestProg(t, "testprog")
655+
if err != nil {
656+
t.Fatal(err)
657+
}
658+
for i := 0; i < 200; i++ {
659+
output := runBuiltTestProg(t, exe, "concurrentMapWrites")
660+
if output == "" {
661+
// If we didn't detect an error, that's ok.
662+
// This case makes this test not flaky like
663+
// the other ones above.
664+
// (More correctly, this case makes this test flaky
665+
// in the other direction, in that it might not
666+
// detect a problem even if there is one.)
667+
continue
668+
}
669+
want := "fatal error: concurrent map writes\n"
670+
if !strings.HasPrefix(output, want) {
671+
t.Fatalf("output does not start with %q:\n%s", want, output)
672+
}
673+
}
674+
}
675+
652676
type point struct {
653677
x, y *int
654678
}

src/runtime/panic.go

+2
Original file line numberDiff line numberDiff line change
@@ -1081,13 +1081,15 @@ func throw(s string) {
10811081
func fatal(s string) {
10821082
// Everything fatal does should be recursively nosplit so it
10831083
// can be called even when it's unsafe to grow the stack.
1084+
printlock() // Prevent multiple interleaved fatal reports. See issue 69447.
10841085
systemstack(func() {
10851086
print("fatal error: ")
10861087
printindented(s) // logically printpanicval(s), but avoids convTstring write barrier
10871088
print("\n")
10881089
})
10891090

10901091
fatalthrow(throwTypeUser)
1092+
printunlock()
10911093
}
10921094

10931095
// runningPanicDefers is non-zero while running deferred functions for panic.

0 commit comments

Comments
 (0)