libgo patch committed: add more preemption checks

Ian Lance Taylor iant@golang.org
Fri Feb 16 16:43:00 GMT 2018


The gofrontend code only preempts goroutines in calls into the
runtime.  This patch adds some more checks for preemption, to catch
some more cases of loops that would otherwise run without
interruption, possibly hanging a program if they are blocking some
other goroutine that could change the exit condition of the loop.  A
particular example is the runtime package benchmark
BenchmarkPingPongHog, so without this patch running the standard
library benchmarks would hang.  Bootstrapped and ran Go testsuite on
x86_64-pc-linux-gnu.  Committed to mainline.

Ian
-------------- next part --------------
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 257637)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-5d5ea2fd05dbf369ccc53c93d4846623cdea0c47
+cef3934fbc63f5e121abb8f88d3799961ac95b59
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/go/runtime/chan.go
===================================================================
--- libgo/go/runtime/chan.go	(revision 257527)
+++ libgo/go/runtime/chan.go	(working copy)
@@ -148,6 +148,11 @@ func chansend1(c *hchan, elem unsafe.Poi
  * the operation; we'll see that it's now closed.
  */
 func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	if c == nil {
 		if !block {
 			return false
@@ -430,6 +435,11 @@ func chanrecv(c *hchan, ep unsafe.Pointe
 		print("chanrecv: chan=", c, "\n")
 	}
 
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	if c == nil {
 		if !block {
 			return
Index: libgo/go/runtime/hashmap.go
===================================================================
--- libgo/go/runtime/hashmap.go	(revision 257527)
+++ libgo/go/runtime/hashmap.go	(working copy)
@@ -356,6 +356,11 @@ func makemap(t *maptype, hint int, h *hm
 // NOTE: The returned pointer may keep the whole map live, so don't
 // hold onto it for very long.
 func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	if raceenabled && h != nil {
 		callerpc := getcallerpc()
 		pc := funcPC(mapaccess1)
@@ -409,6 +414,11 @@ func mapaccess1(t *maptype, h *hmap, key
 }
 
 func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool) {
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	if raceenabled && h != nil {
 		callerpc := getcallerpc()
 		pc := funcPC(mapaccess2)
@@ -463,6 +473,11 @@ func mapaccess2(t *maptype, h *hmap, key
 
 // returns both key and value. Used by map iterator
 func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe.Pointer) {
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	if h == nil || h.count == 0 {
 		return nil, nil
 	}
@@ -521,6 +536,11 @@ func mapaccess2_fat(t *maptype, h *hmap,
 
 // Like mapaccess, but allocates a slot for the key if it is not present in the map.
 func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	if h == nil {
 		panic(plainError("assignment to entry in nil map"))
 	}
@@ -772,6 +792,11 @@ func mapiterinit(t *maptype, h *hmap, it
 }
 
 func mapiternext(it *hiter) {
+	// Check preemption, since unlike gc we don't check on every call.
+	if getg().preempt {
+		checkPreempt()
+	}
+
 	h := it.h
 	if raceenabled {
 		callerpc := getcallerpc()
Index: libgo/go/runtime/malloc.go
===================================================================
--- libgo/go/runtime/malloc.go	(revision 257527)
+++ libgo/go/runtime/malloc.go	(working copy)
@@ -826,6 +826,7 @@ func mallocgc(size uintptr, typ *_type,
 		}
 	}
 
+	// Check preemption, since unlike gc we don't check on every call.
 	if getg().preempt {
 		checkPreempt()
 	}
Index: libgo/go/runtime/proc.go
===================================================================
--- libgo/go/runtime/proc.go	(revision 257527)
+++ libgo/go/runtime/proc.go	(working copy)
@@ -4084,8 +4084,9 @@ func preemptone(_p_ *p) bool {
 	// setting a global variable and figuring out a way to efficiently
 	// check that global variable.
 	//
-	// For now we check gp.preempt in schedule and mallocgc,
-	// which is at least better than doing nothing at all.
+	// For now we check gp.preempt in schedule, mallocgc, selectgo,
+	// and a few other places, which is at least better than doing
+	// nothing at all.
 
 	return true
 }
Index: libgo/go/runtime/select.go
===================================================================
--- libgo/go/runtime/select.go	(revision 257527)
+++ libgo/go/runtime/select.go	(working copy)
@@ -584,6 +584,13 @@ retc:
 	if cas.releasetime > 0 {
 		blockevent(cas.releasetime-t0, 1)
 	}
+
+	// Check preemption, since unlike gc we don't check on every call.
+	// A test case for this one is BenchmarkPingPongHog in proc_test.go.
+	if dfl != nil && getg().preempt {
+		checkPreempt()
+	}
+
 	return casi
 
 sclose:


More information about the Gcc-patches mailing list