Skip to content

Commit 29bbca5

Browse files
committed
runtime: differentiate "user" and "system" throws
"User" throws are throws due to some invariant broken by the application. "System" throws are due to some invariant broken by the runtime, environment, etc (i.e., not the fault of the application). This CL sends "user" throws through the new fatal. Currently this function is identical to throw, but with a different name to clearly differentiate the throw type in the stack trace, and hopefully be a bit more clear to users what it means. This CL changes a few categories of throw to fatal: 1. Concurrent map read/write. 2. Deadlock detection. 3. Unlock of unlocked sync.Mutex. 4. Inconsistent results from syscall.AllThreadsSyscall. "Thread exhaustion" and "out of memory" (usually address space full) throws are additional throws that are arguably the fault of user code, but I've left off for now because there is no specific invariant that they have broken to get into these states. For #51485 Change-Id: I713276a6c290fd34a6563e6e9ef378669d74ae32 Reviewed-on: https://go-review.googlesource.com/c/go/+/390420 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Austin Clements <[email protected]> Run-TryBot: Michael Pratt <[email protected]>
1 parent 4bb45f7 commit 29bbca5

File tree

10 files changed

+76
-46
lines changed

10 files changed

+76
-46
lines changed

src/runtime/HACKING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ avoid allocating in perilous situations. By convention, additional
9090
details are printed before `throw` using `print` or `println` and the
9191
messages are prefixed with "runtime:".
9292

93+
For unrecoverable errors where user code is expected to be at fault for the
94+
failure (such as racing map writes), use `fatal`.
95+
9396
For runtime error debugging, it's useful to run with
9497
`GOTRACEBACK=system` or `GOTRACEBACK=crash`.
9598

src/runtime/map.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
412412
return unsafe.Pointer(&zeroVal[0])
413413
}
414414
if h.flags&hashWriting != 0 {
415-
throw("concurrent map read and map write")
415+
fatal("concurrent map read and map write")
416416
}
417417
hash := t.hasher(key, uintptr(h.hash0))
418418
m := bucketMask(h.B)
@@ -473,7 +473,7 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
473473
return unsafe.Pointer(&zeroVal[0]), false
474474
}
475475
if h.flags&hashWriting != 0 {
476-
throw("concurrent map read and map write")
476+
fatal("concurrent map read and map write")
477477
}
478478
hash := t.hasher(key, uintptr(h.hash0))
479479
m := bucketMask(h.B)
@@ -592,7 +592,7 @@ func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
592592
asanread(key, t.key.size)
593593
}
594594
if h.flags&hashWriting != 0 {
595-
throw("concurrent map writes")
595+
fatal("concurrent map writes")
596596
}
597597
hash := t.hasher(key, uintptr(h.hash0))
598598

@@ -683,7 +683,7 @@ bucketloop:
683683

684684
done:
685685
if h.flags&hashWriting == 0 {
686-
throw("concurrent map writes")
686+
fatal("concurrent map writes")
687687
}
688688
h.flags &^= hashWriting
689689
if t.indirectelem() {
@@ -712,7 +712,7 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
712712
return
713713
}
714714
if h.flags&hashWriting != 0 {
715-
throw("concurrent map writes")
715+
fatal("concurrent map writes")
716716
}
717717

718718
hash := t.hasher(key, uintptr(h.hash0))
@@ -803,7 +803,7 @@ search:
803803
}
804804

805805
if h.flags&hashWriting == 0 {
806-
throw("concurrent map writes")
806+
fatal("concurrent map writes")
807807
}
808808
h.flags &^= hashWriting
809809
}
@@ -870,7 +870,7 @@ func mapiternext(it *hiter) {
870870
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapiternext))
871871
}
872872
if h.flags&hashWriting != 0 {
873-
throw("concurrent map iteration and map write")
873+
fatal("concurrent map iteration and map write")
874874
}
875875
t := it.t
876876
bucket := it.bucket
@@ -1002,7 +1002,7 @@ func mapclear(t *maptype, h *hmap) {
10021002
}
10031003

10041004
if h.flags&hashWriting != 0 {
1005-
throw("concurrent map writes")
1005+
fatal("concurrent map writes")
10061006
}
10071007

10081008
h.flags ^= hashWriting
@@ -1033,7 +1033,7 @@ func mapclear(t *maptype, h *hmap) {
10331033
}
10341034

10351035
if h.flags&hashWriting == 0 {
1036-
throw("concurrent map writes")
1036+
fatal("concurrent map writes")
10371037
}
10381038
h.flags &^= hashWriting
10391039
}

src/runtime/map_fast32.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
1919
return unsafe.Pointer(&zeroVal[0])
2020
}
2121
if h.flags&hashWriting != 0 {
22-
throw("concurrent map read and map write")
22+
fatal("concurrent map read and map write")
2323
}
2424
var b *bmap
2525
if h.B == 0 {
@@ -59,7 +59,7 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
5959
return unsafe.Pointer(&zeroVal[0]), false
6060
}
6161
if h.flags&hashWriting != 0 {
62-
throw("concurrent map read and map write")
62+
fatal("concurrent map read and map write")
6363
}
6464
var b *bmap
6565
if h.B == 0 {
@@ -99,7 +99,7 @@ func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
9999
racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast32))
100100
}
101101
if h.flags&hashWriting != 0 {
102-
throw("concurrent map writes")
102+
fatal("concurrent map writes")
103103
}
104104
hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
105105

@@ -174,7 +174,7 @@ bucketloop:
174174
done:
175175
elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.elemsize))
176176
if h.flags&hashWriting == 0 {
177-
throw("concurrent map writes")
177+
fatal("concurrent map writes")
178178
}
179179
h.flags &^= hashWriting
180180
return elem
@@ -189,7 +189,7 @@ func mapassign_fast32ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer
189189
racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast32))
190190
}
191191
if h.flags&hashWriting != 0 {
192-
throw("concurrent map writes")
192+
fatal("concurrent map writes")
193193
}
194194
hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
195195

@@ -264,7 +264,7 @@ bucketloop:
264264
done:
265265
elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.elemsize))
266266
if h.flags&hashWriting == 0 {
267-
throw("concurrent map writes")
267+
fatal("concurrent map writes")
268268
}
269269
h.flags &^= hashWriting
270270
return elem
@@ -279,7 +279,7 @@ func mapdelete_fast32(t *maptype, h *hmap, key uint32) {
279279
return
280280
}
281281
if h.flags&hashWriting != 0 {
282-
throw("concurrent map writes")
282+
fatal("concurrent map writes")
283283
}
284284

285285
hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
@@ -355,7 +355,7 @@ search:
355355
}
356356

357357
if h.flags&hashWriting == 0 {
358-
throw("concurrent map writes")
358+
fatal("concurrent map writes")
359359
}
360360
h.flags &^= hashWriting
361361
}

src/runtime/map_fast64.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
1919
return unsafe.Pointer(&zeroVal[0])
2020
}
2121
if h.flags&hashWriting != 0 {
22-
throw("concurrent map read and map write")
22+
fatal("concurrent map read and map write")
2323
}
2424
var b *bmap
2525
if h.B == 0 {
@@ -59,7 +59,7 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
5959
return unsafe.Pointer(&zeroVal[0]), false
6060
}
6161
if h.flags&hashWriting != 0 {
62-
throw("concurrent map read and map write")
62+
fatal("concurrent map read and map write")
6363
}
6464
var b *bmap
6565
if h.B == 0 {
@@ -99,7 +99,7 @@ func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
9999
racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast64))
100100
}
101101
if h.flags&hashWriting != 0 {
102-
throw("concurrent map writes")
102+
fatal("concurrent map writes")
103103
}
104104
hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
105105

@@ -174,7 +174,7 @@ bucketloop:
174174
done:
175175
elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.elemsize))
176176
if h.flags&hashWriting == 0 {
177-
throw("concurrent map writes")
177+
fatal("concurrent map writes")
178178
}
179179
h.flags &^= hashWriting
180180
return elem
@@ -189,7 +189,7 @@ func mapassign_fast64ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer
189189
racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast64))
190190
}
191191
if h.flags&hashWriting != 0 {
192-
throw("concurrent map writes")
192+
fatal("concurrent map writes")
193193
}
194194
hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
195195

@@ -264,7 +264,7 @@ bucketloop:
264264
done:
265265
elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.elemsize))
266266
if h.flags&hashWriting == 0 {
267-
throw("concurrent map writes")
267+
fatal("concurrent map writes")
268268
}
269269
h.flags &^= hashWriting
270270
return elem
@@ -279,7 +279,7 @@ func mapdelete_fast64(t *maptype, h *hmap, key uint64) {
279279
return
280280
}
281281
if h.flags&hashWriting != 0 {
282-
throw("concurrent map writes")
282+
fatal("concurrent map writes")
283283
}
284284

285285
hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
@@ -357,7 +357,7 @@ search:
357357
}
358358

359359
if h.flags&hashWriting == 0 {
360-
throw("concurrent map writes")
360+
fatal("concurrent map writes")
361361
}
362362
h.flags &^= hashWriting
363363
}

src/runtime/map_faststr.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
1919
return unsafe.Pointer(&zeroVal[0])
2020
}
2121
if h.flags&hashWriting != 0 {
22-
throw("concurrent map read and map write")
22+
fatal("concurrent map read and map write")
2323
}
2424
key := stringStructOf(&ky)
2525
if h.B == 0 {
@@ -114,7 +114,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
114114
return unsafe.Pointer(&zeroVal[0]), false
115115
}
116116
if h.flags&hashWriting != 0 {
117-
throw("concurrent map read and map write")
117+
fatal("concurrent map read and map write")
118118
}
119119
key := stringStructOf(&ky)
120120
if h.B == 0 {
@@ -209,7 +209,7 @@ func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer {
209209
racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_faststr))
210210
}
211211
if h.flags&hashWriting != 0 {
212-
throw("concurrent map writes")
212+
fatal("concurrent map writes")
213213
}
214214
key := stringStructOf(&s)
215215
hash := t.hasher(noescape(unsafe.Pointer(&s)), uintptr(h.hash0))
@@ -292,7 +292,7 @@ bucketloop:
292292
done:
293293
elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*goarch.PtrSize+inserti*uintptr(t.elemsize))
294294
if h.flags&hashWriting == 0 {
295-
throw("concurrent map writes")
295+
fatal("concurrent map writes")
296296
}
297297
h.flags &^= hashWriting
298298
return elem
@@ -307,7 +307,7 @@ func mapdelete_faststr(t *maptype, h *hmap, ky string) {
307307
return
308308
}
309309
if h.flags&hashWriting != 0 {
310-
throw("concurrent map writes")
310+
fatal("concurrent map writes")
311311
}
312312

313313
key := stringStructOf(&ky)
@@ -383,7 +383,7 @@ search:
383383
}
384384

385385
if h.flags&hashWriting == 0 {
386-
throw("concurrent map writes")
386+
fatal("concurrent map writes")
387387
}
388388
h.flags &^= hashWriting
389389
}

src/runtime/os_linux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,7 @@ func runPerThreadSyscall() {
880880
if errno != 0 || r1 != args.r1 || r2 != args.r2 {
881881
print("trap:", args.trap, ", a123456=[", args.a1, ",", args.a2, ",", args.a3, ",", args.a4, ",", args.a5, ",", args.a6, "]\n")
882882
print("results: got {r1=", r1, ",r2=", r2, ",errno=", errno, "}, want {r1=", args.r1, ",r2=", args.r2, ",errno=0\n")
883-
throw("AllThreadsSyscall6 results differ between threads; runtime corrupted")
883+
fatal("AllThreadsSyscall6 results differ between threads; runtime corrupted")
884884
}
885885

886886
gp.m.needPerThreadSyscall.Store(0)

src/runtime/panic.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -986,19 +986,39 @@ func sync_throw(s string) {
986986
throw(s)
987987
}
988988

989+
//go:linkname sync_fatal sync.fatal
990+
func sync_fatal(s string) {
991+
fatal(s)
992+
}
993+
994+
// throw triggers a fatal error that dumps a stack trace and exits.
995+
//
996+
// throw should be used for runtime-internal fatal errors where Go itself,
997+
// rather than user code, may be at fault for the failure.
989998
//go:nosplit
990999
func throw(s string) {
9911000
// Everything throw does should be recursively nosplit so it
9921001
// can be called even when it's unsafe to grow the stack.
9931002
systemstack(func() {
9941003
print("fatal error: ", s, "\n")
9951004
})
996-
gp := getg()
997-
if gp.m.throwing == 0 {
998-
gp.m.throwing = 1
999-
}
1005+
1006+
fatalthrow()
1007+
}
1008+
1009+
// fatal triggers a fatal error that dumps a stack trace and exits.
1010+
//
1011+
// fatal is equivalent to throw, but is used when user code is expected to be
1012+
// at fault for the failure, such as racing map writes.
1013+
//go:nosplit
1014+
func fatal(s string) {
1015+
// Everything fatal does should be recursively nosplit so it
1016+
// can be called even when it's unsafe to grow the stack.
1017+
systemstack(func() {
1018+
print("fatal error: ", s, "\n")
1019+
})
1020+
10001021
fatalthrow()
1001-
*(*int)(nil) = 0 // not reached
10021022
}
10031023

10041024
// runningPanicDefers is non-zero while running deferred functions for panic.
@@ -1047,8 +1067,13 @@ func fatalthrow() {
10471067
pc := getcallerpc()
10481068
sp := getcallersp()
10491069
gp := getg()
1050-
// Switch to the system stack to avoid any stack growth, which
1051-
// may make things worse if the runtime is in a bad state.
1070+
1071+
if gp.m.throwing == 0 {
1072+
gp.m.throwing = 1
1073+
}
1074+
1075+
// Switch to the system stack to avoid any stack growth, which may make
1076+
// things worse if the runtime is in a bad state.
10521077
systemstack(func() {
10531078
startpanic_m()
10541079

src/runtime/proc.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,7 +4089,7 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
40894089

40904090
if fn == nil {
40914091
_g_.m.throwing = -1 // do not dump full stacks
4092-
throw("go of nil func value")
4092+
fatal("go of nil func value")
40934093
}
40944094
acquirem() // disable preemption because it can be holding p in a local var
40954095

@@ -5012,7 +5012,7 @@ func checkdead() {
50125012
})
50135013
if grunning == 0 { // possible if main goroutine calls runtime·Goexit()
50145014
unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang
5015-
throw("no goroutines (main called runtime.Goexit) - deadlock!")
5015+
fatal("no goroutines (main called runtime.Goexit) - deadlock!")
50165016
}
50175017

50185018
// Maybe jump time forward for playground.
@@ -5047,7 +5047,7 @@ func checkdead() {
50475047

50485048
getg().m.throwing = -1 // do not dump full stacks
50495049
unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang
5050-
throw("all goroutines are asleep - deadlock!")
5050+
fatal("all goroutines are asleep - deadlock!")
50515051
}
50525052

50535053
// forcegcperiod is the maximum time in nanoseconds between garbage

0 commit comments

Comments
 (0)