diff --git a/api/go1.17.txt b/api/go1.17.txt index 3d0a464fec5..48505381f1e 100644 --- a/api/go1.17.txt +++ b/api/go1.17.txt @@ -80,6 +80,7 @@ pkg net/url, method (Values) Has(string) bool pkg reflect, func VisibleFields(Type) []StructField pkg reflect, method (Method) IsExported() bool pkg reflect, method (StructField) IsExported() bool +pkg reflect, method (Value) CanConvert(Type) bool pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete() pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{} diff --git a/doc/go1.17.html b/doc/go1.17.html index b31006fe656..7739d1c62e0 100644 --- a/doc/go1.17.html +++ b/doc/go1.17.html @@ -989,6 +989,18 @@ func Foo() bool {
+ The new
+ Value.CanConvert
+ method reports whether a value can be converted to a type.
+ This may be used to avoid a panic when converting a slice to an
+ array pointer type if the slice is too short.
+ Previously it was sufficient to use
+ Type.ConvertibleTo
+ for this, but the newly permitted conversion from slice to array
+ pointer type can panic even if the types are convertible.
+
The new
StructField.IsExported
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index eaafe792352..b13191f678d 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -2850,3 +2850,35 @@ func TestExecInDeletedDir(t *testing.T) {
// `go version` should not fail
tg.run("version")
}
+
+// A missing C compiler should not force the net package to be stale.
+// Issue 47215.
+func TestMissingCC(t *testing.T) {
+ if !canCgo {
+ t.Skip("test is only meaningful on systems with cgo")
+ }
+ cc := os.Getenv("CC")
+ if cc == "" {
+ cc = "gcc"
+ }
+ if filepath.IsAbs(cc) {
+ t.Skipf(`"CC" (%s) is an absolute path`, cc)
+ }
+ _, err := exec.LookPath(cc)
+ if err != nil {
+ t.Skipf(`"CC" (%s) not on PATH`, cc)
+ }
+
+ tg := testgo(t)
+ defer tg.cleanup()
+ netStale, _ := tg.isStale("net")
+ if netStale {
+ t.Skip(`skipping test because "net" package is currently stale`)
+ }
+
+ tg.setenv("PATH", "") // No C compiler on PATH.
+ netStale, _ = tg.isStale("net")
+ if netStale {
+ t.Error(`clearing "PATH" causes "net" to be stale`)
+ }
+}
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index b506b836561..5a225fb9f1f 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -252,8 +252,15 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
ccExe := b.ccExe()
fmt.Fprintf(h, "CC=%q %q %q %q\n", ccExe, cppflags, cflags, ldflags)
- if ccID, err := b.gccToolID(ccExe[0], "c"); err == nil {
- fmt.Fprintf(h, "CC ID=%q\n", ccID)
+ // Include the C compiler tool ID so that if the C
+ // compiler changes we rebuild the package.
+ // But don't do that for standard library packages like net,
+ // so that the prebuilt .a files from a Go binary install
+ // don't need to be rebuilt with the local compiler.
+ if !p.Standard {
+ if ccID, err := b.gccToolID(ccExe[0], "c"); err == nil {
+ fmt.Fprintf(h, "CC ID=%q\n", ccID)
+ }
}
if len(p.CXXFiles)+len(p.SwigCXXFiles) > 0 {
cxxExe := b.cxxExe()
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index e3faa31c1f7..5e5e4c1e604 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4305,6 +4305,9 @@ func TestConvert(t *testing.T) {
// vout1 represents the in value converted to the in type.
v1 := tt.in
+ if !v1.CanConvert(t1) {
+ t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t1)
+ }
vout1 := v1.Convert(t1)
out1 := vout1.Interface()
if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) {
@@ -4312,6 +4315,9 @@ func TestConvert(t *testing.T) {
}
// vout2 represents the in value converted to the out type.
+ if !v1.CanConvert(t2) {
+ t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t2)
+ }
vout2 := v1.Convert(t2)
out2 := vout2.Interface()
if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) {
@@ -4372,6 +4378,9 @@ func TestConvertPanic(t *testing.T) {
if !v.Type().ConvertibleTo(pt) {
t.Errorf("[]byte should be convertible to *[8]byte")
}
+ if v.CanConvert(pt) {
+ t.Errorf("slice with length 4 should not be convertible to *[8]byte")
+ }
shouldPanic("reflect: cannot convert slice with length 4 to pointer to array with length 8", func() {
_ = v.Convert(pt)
})
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 7589966e222..b4b2d2e38b8 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -2807,6 +2807,26 @@ func (v Value) Convert(t Type) Value {
return op(v, t)
}
+// CanConvert reports whether the value v can be converted to type t.
+// If v.CanConvert(t) returns true then v.Convert(t) will not panic.
+func (v Value) CanConvert(t Type) bool {
+ vt := v.Type()
+ if !vt.ConvertibleTo(t) {
+ return false
+ }
+ // Currently the only conversion that is OK in terms of type
+ // but that can panic depending on the value is converting
+ // from slice to pointer-to-array.
+ if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array {
+ n := t.Elem().Len()
+ h := (*unsafeheader.Slice)(v.ptr)
+ if n > h.Len {
+ return false
+ }
+ }
+ return true
+}
+
// convertOp returns the function to convert a value of type src
// to a value of type dst. If the conversion is illegal, convertOp returns nil.
func convertOp(dst, src *rtype) func(Value, Type) Value {
diff --git a/src/reflect/visiblefields.go b/src/reflect/visiblefields.go
index c068979dcc8..1a2b53570be 100644
--- a/src/reflect/visiblefields.go
+++ b/src/reflect/visiblefields.go
@@ -1,3 +1,7 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
package reflect
// VisibleFields returns all the visible fields in t, which must be a
diff --git a/src/reflect/visiblefields_test.go b/src/reflect/visiblefields_test.go
index 2688b63091a..915bbee867c 100644
--- a/src/reflect/visiblefields_test.go
+++ b/src/reflect/visiblefields_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
package reflect_test
import (
diff --git a/src/runtime/debug/panic_test.go b/src/runtime/debug/panic_test.go
index b93631e1d83..65f9555f376 100644
--- a/src/runtime/debug/panic_test.go
+++ b/src/runtime/debug/panic_test.go
@@ -24,6 +24,9 @@ func TestPanicOnFault(t *testing.T) {
if runtime.GOOS == "ios" {
t.Skip("iOS doesn't provide fault addresses")
}
+ if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm" {
+ t.Skip("netbsd-arm doesn't provide fault address (golang.org/issue/45026)")
+ }
m, err := syscall.Mmap(-1, 0, 0x1000, syscall.PROT_READ /* Note: no PROT_WRITE */, syscall.MAP_SHARED|syscall.MAP_ANON)
if err != nil {
t.Fatalf("can't map anonymous memory: %s", err)
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 8483ec99d88..f8d5d48a28d 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -1126,13 +1126,21 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
msanmalloc(x, size)
}
+ if rate := MemProfileRate; rate > 0 {
+ // Note cache c only valid while m acquired; see #47302
+ if rate != 1 && size < c.nextSample {
+ c.nextSample -= size
+ } else {
+ profilealloc(mp, x, size)
+ }
+ }
mp.mallocing = 0
releasem(mp)
// Pointerfree data can be zeroed late in a context where preemption can occur.
// x will keep the memory alive.
if !isZeroed && needzero {
- memclrNoHeapPointersChunked(size, x)
+ memclrNoHeapPointersChunked(size, x) // This is a possible preemption point: see #47302
}
if debug.malloc {
@@ -1146,16 +1154,6 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
}
}
- if rate := MemProfileRate; rate > 0 {
- if rate != 1 && size < c.nextSample {
- c.nextSample -= size
- } else {
- mp := acquirem()
- profilealloc(mp, x, size)
- releasem(mp)
- }
- }
-
if assistG != nil {
// Account for internal fragmentation in the assist
// debt now that we know it.
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index d557ee8a3e4..83d7d50b192 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -681,7 +681,7 @@ type p struct {
// timerModifiedEarlier status. Because the timer may have been
// modified again, there need not be any timer with this value.
// This is updated using atomic functions.
- // This is 0 if the value is unknown.
+ // This is 0 if there are no timerModifiedEarlier timers.
timerModifiedEarliest uint64
// Per-P GC state
diff --git a/src/runtime/time.go b/src/runtime/time.go
index 90e9b1139f8..2f791c4ad87 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -669,11 +669,6 @@ func adjusttimers(pp *p, now int64) {
if verifyTimers {
verifyTimerHeap(pp)
}
- // There are no timers to adjust, so it is safe to clear
- // timerModifiedEarliest. Do so in case it is stale.
- // Everything will work if we don't do this,
- // but clearing here may save future calls to adjusttimers.
- atomic.Store64(&pp.timerModifiedEarliest, 0)
return
}
diff --git a/src/time/sleep_test.go b/src/time/sleep_test.go
index 6ee0631a855..e0172bf5e0b 100644
--- a/src/time/sleep_test.go
+++ b/src/time/sleep_test.go
@@ -527,6 +527,40 @@ func TestZeroTimer(t *testing.T) {
}
}
+// Test that rapidly moving a timer earlier doesn't cause it to get dropped.
+// Issue 47329.
+func TestTimerModifiedEarlier(t *testing.T) {
+ past := Until(Unix(0, 0))
+ count := 1000
+ fail := 0
+ for i := 0; i < count; i++ {
+ timer := NewTimer(Hour)
+ for j := 0; j < 10; j++ {
+ if !timer.Stop() {
+ <-timer.C
+ }
+ timer.Reset(past)
+ }
+
+ deadline := NewTimer(10 * Second)
+ defer deadline.Stop()
+ now := Now()
+ select {
+ case <-timer.C:
+ if since := Since(now); since > 8*Second {
+ t.Errorf("timer took too long (%v)", since)
+ fail++
+ }
+ case <-deadline.C:
+ t.Error("deadline expired")
+ }
+ }
+
+ if fail > 0 {
+ t.Errorf("%d failures", fail)
+ }
+}
+
// Benchmark timer latency when the thread that creates the timer is busy with
// other work and the timers must be serviced by other threads.
// https://golang.org/issue/38860