mirror of https://github.com/golang/go.git
[release-branch.go1.8] all: merge master into release-branch.go1.8
78860b2ad2cmd/go: don't reject ./... matching top-level file outside GOPATH2b283cedefdatabase/sql: fix race when canceling queries immediately1cf08182f9go/printer: fix format with leading comments in composite literalb531eb3062runtime: reorder modules so main.main comes first165cfbc409database/sql: let tests wait for db pool to come to expected stateea73649343doc: update gccgo docs1db16711f5doc: clarify what to do with Go 1.4 when installing from source3717b429f2doc: note that plugins are not fully baked98842cabb6net/http: don't send body on redirects for 301, 302, 303 when GetBody is set314180e7f6net/http: fix a nitaad06da2b9cmd/link: mark DWARF function symbols as reachablebe9dcfec29doc: mention testing.MainStart signature changea96e117a58runtime: amd64, use 4-byte ops for memmove of 4 bytes4cce27a3facmd/compile: fix constant propagation through s390x MOVDNE instructions1be957d703misc/cgo/test: pass current environment to syscall.Execec654e2251misc/cgo/test: fix test when using GCC 7256a605faacmd/compile: don't use nilcheck information until the next blocke8d5989ed1cmd/compile: fix compilebench -allocea7d9e6a52runtime: check for nil g and m in msanread Change-Id: I61d508d4f0efe4b72e7396645c8ad6088d2bfa6e
This commit is contained in:
commit
2f6c20b46c
|
|
@ -52,6 +52,19 @@ user libraries. The Go 1.4 runtime is not fully merged, but that
|
|||
should not be visible to Go programs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The GCC 6 releases include a complete implementation of the Go 1.6.1
|
||||
user libraries. The Go 1.6 runtime is not fully merged, but that
|
||||
should not be visible to Go programs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The GCC 7 releases are expected to include a complete implementation
|
||||
of the Go 1.8 user libraries. As with earlier releases, the Go 1.8
|
||||
runtime is not fully merged, but that should not be visible to Go
|
||||
programs.
|
||||
</p>
|
||||
|
||||
<h2 id="Source_code">Source code</h2>
|
||||
|
||||
<p>
|
||||
|
|
@ -160,23 +173,6 @@ make
|
|||
make install
|
||||
</pre>
|
||||
|
||||
<h3 id="Ubuntu">A note on Ubuntu</h3>
|
||||
|
||||
<p>
|
||||
Current versions of Ubuntu and versions of GCC before 4.8 disagree on
|
||||
where system libraries and header files are found. This is not a
|
||||
gccgo issue. When building older versions of GCC, setting these
|
||||
environment variables while configuring and building gccgo may fix the
|
||||
problem.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
|
||||
C_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
|
||||
CPLUS_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
|
||||
export LIBRARY_PATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH
|
||||
</pre>
|
||||
|
||||
<h2 id="Using_gccgo">Using gccgo</h2>
|
||||
|
||||
<p>
|
||||
|
|
@ -364,12 +360,15 @@ or with C++ code compiled using <code>extern "C"</code>.
|
|||
<h3 id="Types">Types</h3>
|
||||
|
||||
<p>
|
||||
Basic types map directly: an <code>int</code> in Go is an <code>int</code>
|
||||
in C, an <code>int32</code> is an <code>int32_t</code>,
|
||||
etc. Go <code>byte</code> is equivalent to C <code>unsigned
|
||||
char</code>.
|
||||
Pointers in Go are pointers in C. A Go <code>struct</code> is the same as C
|
||||
<code>struct</code> with the same fields and types.
|
||||
Basic types map directly: an <code>int32</code> in Go is
|
||||
an <code>int32_t</code> in C, an <code>int64</code> is
|
||||
an <code>int64_t</code>, etc.
|
||||
The Go type <code>int</code> is an integer that is the same size as a
|
||||
pointer, and as such corresponds to the C type <code>intptr_t</code>.
|
||||
Go <code>byte</code> is equivalent to C <code>unsigned char</code>.
|
||||
Pointers in Go are pointers in C.
|
||||
A Go <code>struct</code> is the same as C <code>struct</code> with the
|
||||
same fields and types.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -380,7 +379,7 @@ structure (this is <b style="color: red;">subject to change</b>):
|
|||
<pre>
|
||||
struct __go_string {
|
||||
const unsigned char *__data;
|
||||
int __length;
|
||||
intptr_t __length;
|
||||
};
|
||||
</pre>
|
||||
|
||||
|
|
@ -400,8 +399,8 @@ A slice in Go is a structure. The current definition is
|
|||
<pre>
|
||||
struct __go_slice {
|
||||
void *__values;
|
||||
int __count;
|
||||
int __capacity;
|
||||
intptr_t __count;
|
||||
intptr_t __capacity;
|
||||
};
|
||||
</pre>
|
||||
|
||||
|
|
@ -526,15 +525,3 @@ This procedure is full of unstated caveats and restrictions and we make no
|
|||
guarantee that it will not change in the future. It is more useful as a
|
||||
starting point for real Go code than as a regular procedure.
|
||||
</p>
|
||||
|
||||
<h2 id="RTEMS_Port">RTEMS Port</h2>
|
||||
<p>
|
||||
The gccgo compiler has been ported to <a href="http://www.rtems.com/">
|
||||
<code>RTEMS</code></a>. <code>RTEMS</code> is a real-time executive
|
||||
that provides a high performance environment for embedded applications
|
||||
on a range of processors and embedded hardware. The current gccgo
|
||||
port is for x86. The goal is to extend the port to most of the
|
||||
<a href="http://www.rtems.org/wiki/index.php/SupportedCPUs">
|
||||
architectures supported by <code>RTEMS</code></a>. For more information on the port,
|
||||
as well as instructions on how to install it, please see this
|
||||
<a href="http://www.rtems.org/wiki/index.php/GCCGoRTEMS"><code>RTEMS</code> Wiki page</a>.
|
||||
|
|
|
|||
|
|
@ -435,11 +435,11 @@ version of gccgo.
|
|||
<h3 id="plugin">Plugins</h3>
|
||||
|
||||
<p>
|
||||
Go now supports a “<code>plugin</code>” build mode for generating
|
||||
plugins written in Go, and a
|
||||
Go now provides early support for plugins with a “<code>plugin</code>”
|
||||
build mode for generating plugins written in Go, and a
|
||||
new <a href="/pkg/plugin/"><code>plugin</code></a> package for
|
||||
loading such plugins at run time. Plugin support is only currently
|
||||
available on Linux.
|
||||
loading such plugins at run time. Plugin support is currently only
|
||||
available on Linux. Please report any issues.
|
||||
</p>
|
||||
|
||||
<h2 id="runtime">Runtime</h2>
|
||||
|
|
@ -1645,6 +1645,17 @@ crypto/x509: return error for missing SerialNumber (CL 27238)
|
|||
and only the overall execution of the test binary would fail.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 32455 -->
|
||||
The signature of the
|
||||
<a href="/pkg/testing/#MainStart"><code>MainStart</code></a>
|
||||
function has changed, as allowed by the documentation. It is an
|
||||
internal detail and not part of the Go 1 compatibility promise.
|
||||
If you're not calling <code>MainStart</code> directly but see
|
||||
errors, that likely means you set the
|
||||
normally-empty <code>GOROOT</code> environment variable and it
|
||||
doesn't match the version of your <code>go</code> command's binary.
|
||||
</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
|
|
|||
|
|
@ -147,6 +147,9 @@ either the git branch <code>release-branch.go1.4</code> or
|
|||
which contains the Go 1.4 source code plus accumulated fixes
|
||||
to keep the tools running on newer operating systems.
|
||||
(Go 1.4 was the last distribution in which the tool chain was written in C.)
|
||||
After unpacking the Go 1.4 source, <code>cd</code> to
|
||||
the <code>src</code> subdirectory and run <code>make.bash</code> (or,
|
||||
on Windows, <code>make.bat</code>).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ func test18146(t *testing.T) {
|
|||
}
|
||||
runtime.GOMAXPROCS(threads)
|
||||
argv := append(os.Args, "-test.run=NoSuchTestExists")
|
||||
if err := syscall.Exec(os.Args[0], argv, nil); err != nil {
|
||||
if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package cgotest
|
|||
static stack_t oss;
|
||||
static char signalStack[SIGSTKSZ];
|
||||
|
||||
static void changeSignalStack() {
|
||||
static void changeSignalStack(void) {
|
||||
stack_t ss;
|
||||
memset(&ss, 0, sizeof ss);
|
||||
ss.ss_sp = signalStack;
|
||||
|
|
@ -29,7 +29,7 @@ static void changeSignalStack() {
|
|||
}
|
||||
}
|
||||
|
||||
static void restoreSignalStack() {
|
||||
static void restoreSignalStack(void) {
|
||||
#if (defined(__x86_64__) || defined(__i386__)) && defined(__APPLE__)
|
||||
// The Darwin C library enforces a minimum that the kernel does not.
|
||||
// This is OK since we allocated this much space in mpreinit,
|
||||
|
|
@ -42,7 +42,7 @@ static void restoreSignalStack() {
|
|||
}
|
||||
}
|
||||
|
||||
static int zero() {
|
||||
static int zero(void) {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// This program segfaulted during libpreinit when built with -msan:
|
||||
// http://golang.org/issue/18707
|
||||
|
||||
package main
|
||||
|
||||
import "C"
|
||||
|
||||
func main() {}
|
||||
|
|
@ -68,6 +68,25 @@ fi
|
|||
|
||||
status=0
|
||||
|
||||
testmsanshared() {
|
||||
goos=$(go env GOOS)
|
||||
suffix="-installsuffix testsanitizers"
|
||||
libext="so"
|
||||
if [ "$goos" == "darwin" ]; then
|
||||
libext="dylib"
|
||||
fi
|
||||
go build -msan -buildmode=c-shared $suffix -o ${TMPDIR}/libmsanshared.$libext msan_shared.go
|
||||
|
||||
echo 'int main() { return 0; }' > ${TMPDIR}/testmsanshared.c
|
||||
$CC $(go env GOGCCFLAGS) -fsanitize=memory -o ${TMPDIR}/testmsanshared ${TMPDIR}/testmsanshared.c ${TMPDIR}/libmsanshared.$libext
|
||||
|
||||
if ! LD_LIBRARY_PATH=. ${TMPDIR}/testmsanshared; then
|
||||
echo "FAIL: msan_shared"
|
||||
status=1
|
||||
fi
|
||||
rm -f ${TMPDIR}/{testmsanshared,testmsanshared.c,libmsanshared.$libext}
|
||||
}
|
||||
|
||||
if test "$msan" = "yes"; then
|
||||
if ! go build -msan std; then
|
||||
echo "FAIL: build -msan std"
|
||||
|
|
@ -108,6 +127,8 @@ if test "$msan" = "yes"; then
|
|||
echo "FAIL: msan_fail"
|
||||
status=1
|
||||
fi
|
||||
|
||||
testmsanshared
|
||||
fi
|
||||
|
||||
if test "$tsan" = "yes"; then
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"reflect"
|
||||
)
|
||||
|
||||
var SlicePtr interface{} = &[]int{}
|
||||
|
||||
var V int = 1
|
||||
|
||||
var HasMask []string = []string{"hi"}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ func F() *C {
|
|||
return nil
|
||||
}
|
||||
|
||||
var slicePtr interface{} = &[]int{}
|
||||
|
||||
func main() {
|
||||
defer depBase.ImplementedInAsm()
|
||||
// This code below causes various go.itab.* symbols to be generated in
|
||||
|
|
@ -32,4 +34,11 @@ func main() {
|
|||
if reflect.TypeOf(F).Out(0) != reflect.TypeOf(c) {
|
||||
panic("bad reflection results, see golang.org/issue/18252")
|
||||
}
|
||||
|
||||
sp := reflect.New(reflect.TypeOf(slicePtr).Elem())
|
||||
s := sp.Interface()
|
||||
|
||||
if reflect.TypeOf(s) != reflect.TypeOf(slicePtr) {
|
||||
panic("bad reflection results, see golang.org/issue/18729")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,8 +57,13 @@ func startProfile() {
|
|||
Fatalf("%v", err)
|
||||
}
|
||||
atExit(func() {
|
||||
runtime.GC() // profile all outstanding allocations
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
// Profile all outstanding allocations.
|
||||
runtime.GC()
|
||||
// compilebench parses the memory profile to extract memstats,
|
||||
// which are only written in the legacy pprof format.
|
||||
// See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
|
||||
const writeLegacyFormat = 1
|
||||
if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
|
||||
Fatalf("%v", err)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -885,9 +885,9 @@
|
|||
(MOVDEQ y _ (FlagLT)) -> y
|
||||
(MOVDEQ y _ (FlagGT)) -> y
|
||||
|
||||
(MOVDNE _ y (FlagEQ)) -> y
|
||||
(MOVDNE x _ (FlagLT)) -> x
|
||||
(MOVDNE x _ (FlagGT)) -> x
|
||||
(MOVDNE y _ (FlagEQ)) -> y
|
||||
(MOVDNE _ x (FlagLT)) -> x
|
||||
(MOVDNE _ x (FlagGT)) -> x
|
||||
|
||||
(MOVDLT y _ (FlagEQ)) -> y
|
||||
(MOVDLT _ x (FlagLT)) -> x
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func nilcheckelim(f *Func) {
|
|||
}
|
||||
}
|
||||
|
||||
// Next, process values in the block.
|
||||
// Next, eliminate any redundant nil checks in this block.
|
||||
i := 0
|
||||
for _, v := range b.Values {
|
||||
b.Values[i] = v
|
||||
|
|
@ -105,13 +105,10 @@ func nilcheckelim(f *Func) {
|
|||
f.Config.Warnl(v.Line, "removed nil check")
|
||||
}
|
||||
v.reset(OpUnknown)
|
||||
// TODO: f.freeValue(v)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
// Record the fact that we know ptr is non nil, and remember to
|
||||
// undo that information when this dominator subtree is done.
|
||||
nonNilValues[ptr.ID] = true
|
||||
work = append(work, bp{op: ClearPtr, ptr: ptr})
|
||||
}
|
||||
}
|
||||
for j := i; j < len(b.Values); j++ {
|
||||
|
|
@ -119,6 +116,21 @@ func nilcheckelim(f *Func) {
|
|||
}
|
||||
b.Values = b.Values[:i]
|
||||
|
||||
// Finally, find redundant nil checks for subsequent blocks.
|
||||
// Note that we can't add these until the loop above is done, as the
|
||||
// values in the block are not ordered in any way when this pass runs.
|
||||
// This was the cause of issue #18725.
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpNilCheck {
|
||||
continue
|
||||
}
|
||||
ptr := v.Args[0]
|
||||
// Record the fact that we know ptr is non nil, and remember to
|
||||
// undo that information when this dominator subtree is done.
|
||||
nonNilValues[ptr.ID] = true
|
||||
work = append(work, bp{op: ClearPtr, ptr: ptr})
|
||||
}
|
||||
|
||||
// Add all dominated blocks to the work list.
|
||||
for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
|
||||
work = append(work, bp{op: Work, block: w})
|
||||
|
|
|
|||
|
|
@ -9847,11 +9847,11 @@ func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
|
|||
v.AddArg(cmp)
|
||||
return true
|
||||
}
|
||||
// match: (MOVDNE _ y (FlagEQ))
|
||||
// match: (MOVDNE y _ (FlagEQ))
|
||||
// cond:
|
||||
// result: y
|
||||
for {
|
||||
y := v.Args[1]
|
||||
y := v.Args[0]
|
||||
v_2 := v.Args[2]
|
||||
if v_2.Op != OpS390XFlagEQ {
|
||||
break
|
||||
|
|
@ -9861,11 +9861,11 @@ func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
|
|||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
// match: (MOVDNE x _ (FlagLT))
|
||||
// match: (MOVDNE _ x (FlagLT))
|
||||
// cond:
|
||||
// result: x
|
||||
for {
|
||||
x := v.Args[0]
|
||||
x := v.Args[1]
|
||||
v_2 := v.Args[2]
|
||||
if v_2.Op != OpS390XFlagLT {
|
||||
break
|
||||
|
|
@ -9875,11 +9875,11 @@ func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
|
|||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVDNE x _ (FlagGT))
|
||||
// match: (MOVDNE _ x (FlagGT))
|
||||
// cond:
|
||||
// result: x
|
||||
for {
|
||||
x := v.Args[0]
|
||||
x := v.Args[1]
|
||||
v_2 := v.Args[2]
|
||||
if v_2.Op != OpS390XFlagGT {
|
||||
break
|
||||
|
|
|
|||
|
|
@ -3787,3 +3787,26 @@ GLOBL ·constants<>(SB),8,$8
|
|||
tg.setenv("GOPATH", tg.path("go"))
|
||||
tg.run("build", "p")
|
||||
}
|
||||
|
||||
// Issue 18778.
|
||||
func TestDotDotDotOutsideGOPATH(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
tg.tempFile("pkgs/a.go", `package x`)
|
||||
tg.tempFile("pkgs/a_test.go", `package x_test
|
||||
import "testing"
|
||||
func TestX(t *testing.T) {}`)
|
||||
|
||||
tg.tempFile("pkgs/a/a.go", `package a`)
|
||||
tg.tempFile("pkgs/a/a_test.go", `package a_test
|
||||
import "testing"
|
||||
func TestA(t *testing.T) {}`)
|
||||
|
||||
tg.cd(tg.path("pkgs"))
|
||||
tg.run("build", "./...")
|
||||
tg.run("test", "./...")
|
||||
tg.run("list", "./...")
|
||||
tg.grepStdout("pkgs$", "expected package not listed")
|
||||
tg.grepStdout("pkgs/a", "expected package not listed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ func setErrorPos(p *Package, importPos []token.Position) *Package {
|
|||
func cleanImport(path string) string {
|
||||
orig := path
|
||||
path = pathpkg.Clean(path)
|
||||
if strings.HasPrefix(orig, "./") && path != ".." && path != "." && !strings.HasPrefix(path, "../") {
|
||||
if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") {
|
||||
path = "./" + path
|
||||
}
|
||||
return path
|
||||
|
|
|
|||
|
|
@ -1080,7 +1080,7 @@ func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
|
|||
epcs = s
|
||||
|
||||
dsym := ctxt.Syms.Lookup(dwarf.InfoPrefix+s.Name, int(s.Version))
|
||||
dsym.Attr |= AttrHidden
|
||||
dsym.Attr |= AttrHidden | AttrReachable
|
||||
dsym.Type = obj.SDWARFINFO
|
||||
for _, r := range dsym.R {
|
||||
if r.Type == obj.R_DWARFREF && r.Sym.Size == 0 {
|
||||
|
|
|
|||
|
|
@ -1357,16 +1357,7 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
|
|||
cancel: cancel,
|
||||
ctx: ctx,
|
||||
}
|
||||
go func(tx *Tx) {
|
||||
select {
|
||||
case <-tx.ctx.Done():
|
||||
if !tx.isDone() {
|
||||
// Discard and close the connection used to ensure the transaction
|
||||
// is closed and the resources are released.
|
||||
tx.rollback(true)
|
||||
}
|
||||
}
|
||||
}(tx)
|
||||
go tx.awaitDone()
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
|
|
@ -1388,6 +1379,11 @@ func (db *DB) Driver() driver.Driver {
|
|||
type Tx struct {
|
||||
db *DB
|
||||
|
||||
// closemu prevents the transaction from closing while there
|
||||
// is an active query. It is held for read during queries
|
||||
// and exclusively during close.
|
||||
closemu sync.RWMutex
|
||||
|
||||
// dc is owned exclusively until Commit or Rollback, at which point
|
||||
// it's returned with putConn.
|
||||
dc *driverConn
|
||||
|
|
@ -1413,6 +1409,20 @@ type Tx struct {
|
|||
ctx context.Context
|
||||
}
|
||||
|
||||
// awaitDone blocks until the context in Tx is canceled and rolls back
|
||||
// the transaction if it's not already done.
|
||||
func (tx *Tx) awaitDone() {
|
||||
// Wait for either the transaction to be committed or rolled
|
||||
// back, or for the associated context to be closed.
|
||||
<-tx.ctx.Done()
|
||||
|
||||
// Discard and close the connection used to ensure the
|
||||
// transaction is closed and the resources are released. This
|
||||
// rollback does nothing if the transaction has already been
|
||||
// committed or rolled back.
|
||||
tx.rollback(true)
|
||||
}
|
||||
|
||||
func (tx *Tx) isDone() bool {
|
||||
return atomic.LoadInt32(&tx.done) != 0
|
||||
}
|
||||
|
|
@ -1424,16 +1434,31 @@ var ErrTxDone = errors.New("sql: Transaction has already been committed or rolle
|
|||
// close returns the connection to the pool and
|
||||
// must only be called by Tx.rollback or Tx.Commit.
|
||||
func (tx *Tx) close(err error) {
|
||||
tx.closemu.Lock()
|
||||
defer tx.closemu.Unlock()
|
||||
|
||||
tx.db.putConn(tx.dc, err)
|
||||
tx.cancel()
|
||||
tx.dc = nil
|
||||
tx.txi = nil
|
||||
}
|
||||
|
||||
// hookTxGrabConn specifies an optional hook to be called on
|
||||
// a successful call to (*Tx).grabConn. For tests.
|
||||
var hookTxGrabConn func()
|
||||
|
||||
func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
if tx.isDone() {
|
||||
return nil, ErrTxDone
|
||||
}
|
||||
if hookTxGrabConn != nil { // test hook
|
||||
hookTxGrabConn()
|
||||
}
|
||||
return tx.dc, nil
|
||||
}
|
||||
|
||||
|
|
@ -1503,6 +1528,9 @@ func (tx *Tx) Rollback() error {
|
|||
// for the execution of the returned statement. The returned statement
|
||||
// will run in the transaction context.
|
||||
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
|
||||
tx.closemu.RLock()
|
||||
defer tx.closemu.RUnlock()
|
||||
|
||||
// TODO(bradfitz): We could be more efficient here and either
|
||||
// provide a method to take an existing Stmt (created on
|
||||
// perhaps a different Conn), and re-create it on this Conn if
|
||||
|
|
@ -1567,6 +1595,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
|
|||
// The returned statement operates within the transaction and will be closed
|
||||
// when the transaction has been committed or rolled back.
|
||||
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
|
||||
tx.closemu.RLock()
|
||||
defer tx.closemu.RUnlock()
|
||||
|
||||
// TODO(bradfitz): optimize this. Currently this re-prepares
|
||||
// each time. This is fine for now to illustrate the API but
|
||||
// we should really cache already-prepared statements
|
||||
|
|
@ -1618,6 +1649,9 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
|
|||
// ExecContext executes a query that doesn't return rows.
|
||||
// For example: an INSERT and UPDATE.
|
||||
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
|
||||
tx.closemu.RLock()
|
||||
defer tx.closemu.RUnlock()
|
||||
|
||||
dc, err := tx.grabConn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1661,6 +1695,9 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
|
|||
|
||||
// QueryContext executes a query that returns rows, typically a SELECT.
|
||||
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
|
||||
tx.closemu.RLock()
|
||||
defer tx.closemu.RUnlock()
|
||||
|
||||
dc, err := tx.grabConn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -2038,25 +2075,21 @@ type Rows struct {
|
|||
// closed value is 1 when the Rows is closed.
|
||||
// Use atomic operations on value when checking value.
|
||||
closed int32
|
||||
ctxClose chan struct{} // closed when Rows is closed, may be null.
|
||||
cancel func() // called when Rows is closed, may be nil.
|
||||
lastcols []driver.Value
|
||||
lasterr error // non-nil only if closed is true
|
||||
closeStmt *driverStmt // if non-nil, statement to Close on close
|
||||
}
|
||||
|
||||
func (rs *Rows) initContextClose(ctx context.Context) {
|
||||
if ctx.Done() == context.Background().Done() {
|
||||
return
|
||||
}
|
||||
ctx, rs.cancel = context.WithCancel(ctx)
|
||||
go rs.awaitDone(ctx)
|
||||
}
|
||||
|
||||
rs.ctxClose = make(chan struct{})
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
rs.Close()
|
||||
case <-rs.ctxClose:
|
||||
}
|
||||
}()
|
||||
// awaitDone blocks until the rows are closed or the context canceled.
|
||||
func (rs *Rows) awaitDone(ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
rs.Close()
|
||||
}
|
||||
|
||||
// Next prepares the next result row for reading with the Scan method. It
|
||||
|
|
@ -2314,7 +2347,9 @@ func (rs *Rows) Scan(dest ...interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var rowsCloseHook func(*Rows, *error)
|
||||
// rowsCloseHook returns a function so tests may install the
|
||||
// hook throug a test only mutex.
|
||||
var rowsCloseHook = func() func(*Rows, *error) { return nil }
|
||||
|
||||
func (rs *Rows) isClosed() bool {
|
||||
return atomic.LoadInt32(&rs.closed) != 0
|
||||
|
|
@ -2328,13 +2363,15 @@ func (rs *Rows) Close() error {
|
|||
if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
if rs.ctxClose != nil {
|
||||
close(rs.ctxClose)
|
||||
}
|
||||
|
||||
err := rs.rowsi.Close()
|
||||
if fn := rowsCloseHook; fn != nil {
|
||||
if fn := rowsCloseHook(); fn != nil {
|
||||
fn(rs, &err)
|
||||
}
|
||||
if rs.cancel != nil {
|
||||
rs.cancel()
|
||||
}
|
||||
|
||||
if rs.closeStmt != nil {
|
||||
rs.closeStmt.Close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -326,9 +327,7 @@ func TestQueryContext(t *testing.T) {
|
|||
|
||||
// And verify that the final rows.Next() call, which hit EOF,
|
||||
// also closed the rows connection.
|
||||
if n := db.numFreeConns(); n != 1 {
|
||||
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
|
||||
}
|
||||
waitForFree(t, db, 5*time.Second, 1)
|
||||
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
|
||||
t.Errorf("executed %d Prepare statements; want 1", prepares)
|
||||
}
|
||||
|
|
@ -345,6 +344,18 @@ func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// waitForFree checks db.numFreeConns until either it equals want or
|
||||
// the maxWait time elapses.
|
||||
func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
|
||||
var numFree int
|
||||
if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
|
||||
numFree = db.numFreeConns()
|
||||
return numFree == want
|
||||
}) {
|
||||
t.Fatalf("free conns after hitting EOF = %d; want %d", numFree, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryContextWait(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
|
@ -361,9 +372,7 @@ func TestQueryContextWait(t *testing.T) {
|
|||
}
|
||||
|
||||
// Verify closed rows connection after error condition.
|
||||
if n := db.numFreeConns(); n != 1 {
|
||||
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
|
||||
}
|
||||
waitForFree(t, db, 5*time.Second, 1)
|
||||
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
|
||||
t.Errorf("executed %d Prepare statements; want 1", prepares)
|
||||
}
|
||||
|
|
@ -388,13 +397,7 @@ func TestTxContextWait(t *testing.T) {
|
|||
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
|
||||
}
|
||||
|
||||
var numFree int
|
||||
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
|
||||
numFree = db.numFreeConns()
|
||||
return numFree == 0
|
||||
}) {
|
||||
t.Fatalf("free conns after hitting EOF = %d; want 0", numFree)
|
||||
}
|
||||
waitForFree(t, db, 5*time.Second, 0)
|
||||
|
||||
// Ensure the dropped connection allows more connections to be made.
|
||||
// Checked on DB Close.
|
||||
|
|
@ -471,9 +474,7 @@ func TestMultiResultSetQuery(t *testing.T) {
|
|||
|
||||
// And verify that the final rows.Next() call, which hit EOF,
|
||||
// also closed the rows connection.
|
||||
if n := db.numFreeConns(); n != 1 {
|
||||
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
|
||||
}
|
||||
waitForFree(t, db, 5*time.Second, 1)
|
||||
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
|
||||
t.Errorf("executed %d Prepare statements; want 1", prepares)
|
||||
}
|
||||
|
|
@ -1135,6 +1136,24 @@ func TestQueryRowClosingStmt(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var atomicRowsCloseHook atomic.Value // of func(*Rows, *error)
|
||||
|
||||
func init() {
|
||||
rowsCloseHook = func() func(*Rows, *error) {
|
||||
fn, _ := atomicRowsCloseHook.Load().(func(*Rows, *error))
|
||||
return fn
|
||||
}
|
||||
}
|
||||
|
||||
func setRowsCloseHook(fn func(*Rows, *error)) {
|
||||
if fn == nil {
|
||||
// Can't change an atomic.Value back to nil, so set it to this
|
||||
// no-op func instead.
|
||||
fn = func(*Rows, *error) {}
|
||||
}
|
||||
atomicRowsCloseHook.Store(fn)
|
||||
}
|
||||
|
||||
// Test issue 6651
|
||||
func TestIssue6651(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
|
|
@ -1147,6 +1166,7 @@ func TestIssue6651(t *testing.T) {
|
|||
return fmt.Errorf(want)
|
||||
}
|
||||
defer func() { rowsCursorNextHook = nil }()
|
||||
|
||||
err := db.QueryRow("SELECT|people|name|").Scan(&v)
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error = %q; want %q", err, want)
|
||||
|
|
@ -1154,10 +1174,10 @@ func TestIssue6651(t *testing.T) {
|
|||
rowsCursorNextHook = nil
|
||||
|
||||
want = "error in rows.Close"
|
||||
rowsCloseHook = func(rows *Rows, err *error) {
|
||||
setRowsCloseHook(func(rows *Rows, err *error) {
|
||||
*err = fmt.Errorf(want)
|
||||
}
|
||||
defer func() { rowsCloseHook = nil }()
|
||||
})
|
||||
defer setRowsCloseHook(nil)
|
||||
err = db.QueryRow("SELECT|people|name|").Scan(&v)
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error = %q; want %q", err, want)
|
||||
|
|
@ -1830,7 +1850,9 @@ func TestStmtCloseDeps(t *testing.T) {
|
|||
db.dumpDeps(t)
|
||||
}
|
||||
|
||||
if len(stmt.css) > nquery {
|
||||
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
|
||||
return len(stmt.css) <= nquery
|
||||
}) {
|
||||
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
|
||||
}
|
||||
|
||||
|
|
@ -2576,10 +2598,10 @@ func TestIssue6081(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rowsCloseHook = func(rows *Rows, err *error) {
|
||||
setRowsCloseHook(func(rows *Rows, err *error) {
|
||||
*err = driver.ErrBadConn
|
||||
}
|
||||
defer func() { rowsCloseHook = nil }()
|
||||
})
|
||||
defer setRowsCloseHook(nil)
|
||||
for i := 0; i < 10; i++ {
|
||||
rows, err := stmt.Query()
|
||||
if err != nil {
|
||||
|
|
@ -2642,7 +2664,10 @@ func TestIssue18429(t *testing.T) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
rows, err := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
|
||||
// This is expected to give a cancel error many, but not all the time.
|
||||
// Test failure will happen with a panic or other race condition being
|
||||
// reported.
|
||||
rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
|
||||
if rows != nil {
|
||||
rows.Close()
|
||||
}
|
||||
|
|
@ -2655,6 +2680,56 @@ func TestIssue18429(t *testing.T) {
|
|||
time.Sleep(milliWait * 3 * time.Millisecond)
|
||||
}
|
||||
|
||||
// TestIssue18719 closes the context right before use. The sql.driverConn
|
||||
// will nil out the ci on close in a lock, but if another process uses it right after
|
||||
// it will panic with on the nil ref.
|
||||
//
|
||||
// See https://golang.org/cl/35550 .
|
||||
func TestIssue18719(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hookTxGrabConn = func() {
|
||||
cancel()
|
||||
|
||||
// Wait for the context to cancel and tx to rollback.
|
||||
for tx.isDone() == false {
|
||||
time.Sleep(time.Millisecond * 3)
|
||||
}
|
||||
}
|
||||
defer func() { hookTxGrabConn = nil }()
|
||||
|
||||
// This call will grab the connection and cancel the context
|
||||
// after it has done so. Code after must deal with the canceled state.
|
||||
rows, err := tx.QueryContext(ctx, "SELECT|people|name|")
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
t.Fatalf("expected error %v but got %v", nil, err)
|
||||
}
|
||||
|
||||
// Rows may be ignored because it will be closed when the context is canceled.
|
||||
|
||||
// Do not explicitly rollback. The rollback will happen from the
|
||||
// canceled context.
|
||||
|
||||
// Wait for connections to return to pool.
|
||||
var numOpen int
|
||||
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
|
||||
numOpen = db.numOpenConns()
|
||||
return numOpen == 0
|
||||
}) {
|
||||
t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrency(t *testing.T) {
|
||||
doConcurrentTest(t, new(concurrentDBQueryTest))
|
||||
doConcurrentTest(t, new(concurrentDBExecTest))
|
||||
|
|
|
|||
|
|
@ -733,7 +733,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
|
|||
|
||||
case *ast.FuncLit:
|
||||
p.expr(x.Type)
|
||||
p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
|
||||
p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)
|
||||
|
||||
case *ast.ParenExpr:
|
||||
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
|
||||
|
|
@ -825,6 +825,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
|
|||
if x.Type != nil {
|
||||
p.expr1(x.Type, token.HighestPrec, depth)
|
||||
}
|
||||
p.level++
|
||||
p.print(x.Lbrace, token.LBRACE)
|
||||
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
|
||||
// do not insert extra line break following a /*-style comment
|
||||
|
|
@ -837,6 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
|
|||
mode |= noExtraBlank
|
||||
}
|
||||
p.print(mode, x.Rbrace, token.RBRACE, mode)
|
||||
p.level--
|
||||
|
||||
case *ast.Ellipsis:
|
||||
p.print(token.ELLIPSIS)
|
||||
|
|
@ -1557,18 +1559,23 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
|
|||
return bodySize
|
||||
}
|
||||
|
||||
// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
|
||||
// a header (e.g., a for-loop control clause or function signature) of given headerSize.
|
||||
// funcBody prints a function body following a function header of given headerSize.
|
||||
// If the header's and block's size are "small enough" and the block is "simple enough",
|
||||
// the block is printed on the current line, without line breaks, spaced from the header
|
||||
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
|
||||
// lines for the block's statements and its closing "}".
|
||||
//
|
||||
func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
|
||||
func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// save/restore composite literal nesting level
|
||||
defer func(level int) {
|
||||
p.level = level
|
||||
}(p.level)
|
||||
p.level = 0
|
||||
|
||||
const maxSize = 100
|
||||
if headerSize+p.bodySize(b, maxSize) <= maxSize {
|
||||
p.print(sep, b.Lbrace, token.LBRACE)
|
||||
|
|
@ -1613,7 +1620,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
|
|||
}
|
||||
p.expr(d.Name)
|
||||
p.signature(d.Type.Params, d.Type.Results)
|
||||
p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
|
||||
p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
|
||||
}
|
||||
|
||||
func (p *printer) decl(decl ast.Decl) {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ type printer struct {
|
|||
// Current state
|
||||
output []byte // raw printer result
|
||||
indent int // current indentation
|
||||
level int // level == 0: outside composite literal; level > 0: inside composite literal
|
||||
mode pmode // current printer mode
|
||||
impliedSemi bool // if set, a linebreak implies a semicolon
|
||||
lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace)
|
||||
|
|
@ -744,15 +745,19 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
|
|||
// follows on the same line but is not a comma, and not a "closing"
|
||||
// token immediately following its corresponding "opening" token,
|
||||
// add an extra separator unless explicitly disabled. Use a blank
|
||||
// as separator unless we have pending linebreaks and they are not
|
||||
// disabled, in which case we want a linebreak (issue 15137).
|
||||
// as separator unless we have pending linebreaks, they are not
|
||||
// disabled, and we are outside a composite literal, in which case
|
||||
// we want a linebreak (issue 15137).
|
||||
// TODO(gri) This has become overly complicated. We should be able
|
||||
// to track whether we're inside an expression or statement and
|
||||
// use that information to decide more directly.
|
||||
needsLinebreak := false
|
||||
if p.mode&noExtraBlank == 0 &&
|
||||
last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
|
||||
tok != token.COMMA &&
|
||||
(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
|
||||
(tok != token.RBRACK || p.prevOpen == token.LBRACK) {
|
||||
if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 {
|
||||
if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {
|
||||
needsLinebreak = true
|
||||
} else {
|
||||
p.writeByte(' ', 1)
|
||||
|
|
|
|||
|
|
@ -103,3 +103,62 @@ label:
|
|||
mask := uint64(1)<<c - 1 // Allocation mask
|
||||
used := atomic.LoadUint64(&h.used) // Current allocations
|
||||
}
|
||||
|
||||
// Test cases for issue 18782
|
||||
var _ = [][]int{
|
||||
/* a, b, c, d, e */
|
||||
/* a */ {0, 0, 0, 0, 0},
|
||||
/* b */ {0, 5, 4, 4, 4},
|
||||
/* c */ {0, 4, 5, 4, 4},
|
||||
/* d */ {0, 4, 4, 5, 4},
|
||||
/* e */ {0, 4, 4, 4, 5},
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ 0}
|
||||
|
||||
var _ = T{ /* a */ /* b */ 0}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */ 0,
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */
|
||||
/* d */ 0,
|
||||
}
|
||||
|
||||
var _ = T{
|
||||
/* a */
|
||||
/* b */ 0,
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ {}}
|
||||
|
||||
var _ = T{ /* a */ /* b */ {}}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */ {},
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */
|
||||
/* d */ {},
|
||||
}
|
||||
|
||||
var _ = T{
|
||||
/* a */
|
||||
/* b */ {},
|
||||
}
|
||||
|
||||
var _ = []T{
|
||||
func() {
|
||||
var _ = [][]int{
|
||||
/* a, b, c, d, e */
|
||||
/* a */ {0, 0, 0, 0, 0},
|
||||
/* b */ {0, 5, 4, 4, 4},
|
||||
/* c */ {0, 4, 5, 4, 4},
|
||||
/* d */ {0, 4, 4, 5, 4},
|
||||
/* e */ {0, 4, 4, 4, 5},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,3 +103,66 @@ label:
|
|||
mask := uint64(1)<<c - 1 // Allocation mask
|
||||
used := atomic.LoadUint64(&h.used) // Current allocations
|
||||
}
|
||||
|
||||
// Test cases for issue 18782
|
||||
var _ = [][]int{
|
||||
/* a, b, c, d, e */
|
||||
/* a */ {0, 0, 0, 0, 0},
|
||||
/* b */ {0, 5, 4, 4, 4},
|
||||
/* c */ {0, 4, 5, 4, 4},
|
||||
/* d */ {0, 4, 4, 5, 4},
|
||||
/* e */ {0, 4, 4, 4, 5},
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ 0,
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */ 0,
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */ 0,
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */
|
||||
/* d */ 0,
|
||||
}
|
||||
|
||||
var _ = T{
|
||||
/* a */
|
||||
/* b */ 0,
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ {},
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */ {},
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */ {},
|
||||
}
|
||||
|
||||
var _ = T{ /* a */ /* b */
|
||||
/* c */
|
||||
/* d */ {},
|
||||
}
|
||||
|
||||
var _ = T{
|
||||
/* a */
|
||||
/* b */ {},
|
||||
}
|
||||
|
||||
var _ = []T{
|
||||
func() {
|
||||
var _ = [][]int{
|
||||
/* a, b, c, d, e */
|
||||
/* a */ {0, 0, 0, 0, 0},
|
||||
/* b */ {0, 5, 4, 4, 4},
|
||||
/* c */ {0, 4, 5, 4, 4},
|
||||
/* d */ {0, 4, 4, 5, 4},
|
||||
/* e */ {0, 4, 4, 4, 5},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,11 +413,12 @@ func (c *Client) checkRedirect(req *Request, via []*Request) error {
|
|||
|
||||
// redirectBehavior describes what should happen when the
|
||||
// client encounters a 3xx status code from the server
|
||||
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect bool) {
|
||||
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
|
||||
switch resp.StatusCode {
|
||||
case 301, 302, 303:
|
||||
redirectMethod = reqMethod
|
||||
shouldRedirect = true
|
||||
includeBody = false
|
||||
|
||||
// RFC 2616 allowed automatic redirection only with GET and
|
||||
// HEAD requests. RFC 7231 lifts this restriction, but we still
|
||||
|
|
@ -429,6 +430,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect
|
|||
case 307, 308:
|
||||
redirectMethod = reqMethod
|
||||
shouldRedirect = true
|
||||
includeBody = true
|
||||
|
||||
// Treat 307 and 308 specially, since they're new in
|
||||
// Go 1.8, and they also require re-sending the request body.
|
||||
|
|
@ -449,7 +451,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect
|
|||
shouldRedirect = false
|
||||
}
|
||||
}
|
||||
return redirectMethod, shouldRedirect
|
||||
return redirectMethod, shouldRedirect, includeBody
|
||||
}
|
||||
|
||||
// Do sends an HTTP request and returns an HTTP response, following
|
||||
|
|
@ -492,11 +494,14 @@ func (c *Client) Do(req *Request) (*Response, error) {
|
|||
}
|
||||
|
||||
var (
|
||||
deadline = c.deadline()
|
||||
reqs []*Request
|
||||
resp *Response
|
||||
copyHeaders = c.makeHeadersCopier(req)
|
||||
deadline = c.deadline()
|
||||
reqs []*Request
|
||||
resp *Response
|
||||
copyHeaders = c.makeHeadersCopier(req)
|
||||
|
||||
// Redirect behavior:
|
||||
redirectMethod string
|
||||
includeBody bool
|
||||
)
|
||||
uerr := func(err error) error {
|
||||
req.closeBody()
|
||||
|
|
@ -534,7 +539,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
|
|||
Cancel: ireq.Cancel,
|
||||
ctx: ireq.ctx,
|
||||
}
|
||||
if ireq.GetBody != nil {
|
||||
if includeBody && ireq.GetBody != nil {
|
||||
req.Body, err = ireq.GetBody()
|
||||
if err != nil {
|
||||
return nil, uerr(err)
|
||||
|
|
@ -598,7 +603,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
|
|||
}
|
||||
|
||||
var shouldRedirect bool
|
||||
redirectMethod, shouldRedirect = redirectBehavior(req.Method, resp, reqs[0])
|
||||
redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])
|
||||
if !shouldRedirect {
|
||||
return resp, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,25 +360,25 @@ func TestPostRedirects(t *testing.T) {
|
|||
wantSegments := []string{
|
||||
`POST / "first"`,
|
||||
`POST /?code=301&next=302 "c301"`,
|
||||
`GET /?code=302 "c301"`,
|
||||
`GET / "c301"`,
|
||||
`GET /?code=302 ""`,
|
||||
`GET / ""`,
|
||||
`POST /?code=302&next=302 "c302"`,
|
||||
`GET /?code=302 "c302"`,
|
||||
`GET / "c302"`,
|
||||
`GET /?code=302 ""`,
|
||||
`GET / ""`,
|
||||
`POST /?code=303&next=301 "c303wc301"`,
|
||||
`GET /?code=301 "c303wc301"`,
|
||||
`GET / "c303wc301"`,
|
||||
`GET /?code=301 ""`,
|
||||
`GET / ""`,
|
||||
`POST /?code=304 "c304"`,
|
||||
`POST /?code=305 "c305"`,
|
||||
`POST /?code=307&next=303,308,302 "c307"`,
|
||||
`POST /?code=303&next=308,302 "c307"`,
|
||||
`GET /?code=308&next=302 "c307"`,
|
||||
`GET /?code=308&next=302 ""`,
|
||||
`GET /?code=302 "c307"`,
|
||||
`GET / "c307"`,
|
||||
`GET / ""`,
|
||||
`POST /?code=308&next=302,301 "c308"`,
|
||||
`POST /?code=302&next=301 "c308"`,
|
||||
`GET /?code=301 "c308"`,
|
||||
`GET / "c308"`,
|
||||
`GET /?code=301 ""`,
|
||||
`GET / ""`,
|
||||
`POST /?code=404 "c404"`,
|
||||
}
|
||||
want := strings.Join(wantSegments, "\n")
|
||||
|
|
@ -399,20 +399,20 @@ func TestDeleteRedirects(t *testing.T) {
|
|||
wantSegments := []string{
|
||||
`DELETE / "first"`,
|
||||
`DELETE /?code=301&next=302,308 "c301"`,
|
||||
`GET /?code=302&next=308 "c301"`,
|
||||
`GET /?code=308 "c301"`,
|
||||
`GET /?code=302&next=308 ""`,
|
||||
`GET /?code=308 ""`,
|
||||
`GET / "c301"`,
|
||||
`DELETE /?code=302&next=302 "c302"`,
|
||||
`GET /?code=302 "c302"`,
|
||||
`GET / "c302"`,
|
||||
`GET /?code=302 ""`,
|
||||
`GET / ""`,
|
||||
`DELETE /?code=303 "c303"`,
|
||||
`GET / "c303"`,
|
||||
`GET / ""`,
|
||||
`DELETE /?code=307&next=301,308,303,302,304 "c307"`,
|
||||
`DELETE /?code=301&next=308,303,302,304 "c307"`,
|
||||
`GET /?code=308&next=303,302,304 "c307"`,
|
||||
`GET /?code=308&next=303,302,304 ""`,
|
||||
`GET /?code=303&next=302,304 "c307"`,
|
||||
`GET /?code=302&next=304 "c307"`,
|
||||
`GET /?code=304 "c307"`,
|
||||
`GET /?code=302&next=304 ""`,
|
||||
`GET /?code=304 ""`,
|
||||
`DELETE /?code=308&next=307 "c308"`,
|
||||
`DELETE /?code=307 "c308"`,
|
||||
`DELETE / "c308"`,
|
||||
|
|
@ -432,7 +432,11 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
|
|||
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
log.Lock()
|
||||
slurp, _ := ioutil.ReadAll(r.Body)
|
||||
fmt.Fprintf(&log.Buffer, "%s %s %q\n", r.Method, r.RequestURI, slurp)
|
||||
fmt.Fprintf(&log.Buffer, "%s %s %q", r.Method, r.RequestURI, slurp)
|
||||
if cl := r.Header.Get("Content-Length"); r.Method == "GET" && len(slurp) == 0 && (r.ContentLength != 0 || cl != "") {
|
||||
fmt.Fprintf(&log.Buffer, " (but with body=%T, content-length = %v, %q)", r.Body, r.ContentLength, cl)
|
||||
}
|
||||
log.WriteByte('\n')
|
||||
log.Unlock()
|
||||
urlQuery := r.URL.Query()
|
||||
if v := urlQuery.Get("code"); v != "" {
|
||||
|
|
@ -475,7 +479,24 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
|
|||
want = strings.TrimSpace(want)
|
||||
|
||||
if got != want {
|
||||
t.Errorf("Log differs.\n Got:\n%s\nWant:\n%s\n", got, want)
|
||||
got, want, lines := removeCommonLines(got, want)
|
||||
t.Errorf("Log differs after %d common lines.\n\nGot:\n%s\n\nWant:\n%s\n", lines, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func removeCommonLines(a, b string) (asuffix, bsuffix string, commonLines int) {
|
||||
for {
|
||||
nl := strings.IndexByte(a, '\n')
|
||||
if nl < 0 {
|
||||
return a, b, commonLines
|
||||
}
|
||||
line := a[:nl+1]
|
||||
if !strings.HasPrefix(b, line) {
|
||||
return a, b, commonLines
|
||||
}
|
||||
commonLines++
|
||||
a = a[len(line):]
|
||||
b = b[len(line):]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5277,7 +5277,7 @@ func TestServerHijackGetsBackgroundByte_big(t *testing.T) {
|
|||
defer conn.Close()
|
||||
slurp, err := ioutil.ReadAll(buf.Reader)
|
||||
if err != nil {
|
||||
t.Error("Copy: %v", err)
|
||||
t.Errorf("Copy: %v", err)
|
||||
}
|
||||
allX := true
|
||||
for _, v := range slurp {
|
||||
|
|
|
|||
|
|
@ -146,10 +146,16 @@ move_1or2:
|
|||
move_0:
|
||||
RET
|
||||
move_3or4:
|
||||
CMPQ BX, $4
|
||||
JB move_3
|
||||
MOVL (SI), AX
|
||||
MOVL AX, (DI)
|
||||
RET
|
||||
move_3:
|
||||
MOVW (SI), AX
|
||||
MOVW -2(SI)(BX*1), CX
|
||||
MOVB 2(SI), CX
|
||||
MOVW AX, (DI)
|
||||
MOVW CX, -2(DI)(BX*1)
|
||||
MOVB CX, 2(DI)
|
||||
RET
|
||||
move_5through7:
|
||||
MOVL (SI), AX
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"internal/race"
|
||||
. "runtime"
|
||||
|
|
@ -447,3 +448,22 @@ func BenchmarkCopyFat1024(b *testing.B) {
|
|||
_ = y
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIssue18740(b *testing.B) {
|
||||
// This tests that memmove uses one 4-byte load/store to move 4 bytes.
|
||||
// It used to do 2 2-byte load/stores, which leads to a pipeline stall
|
||||
// when we try to read the result with one 4-byte load.
|
||||
var buf [4]byte
|
||||
for j := 0; j < b.N; j++ {
|
||||
s := uint32(0)
|
||||
for i := 0; i < 4096; i += 4 {
|
||||
copy(buf[:], g[i:])
|
||||
s += binary.LittleEndian.Uint32(buf[:])
|
||||
}
|
||||
sink = uint64(s)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 2 byte and 8 byte benchmarks also.
|
||||
|
||||
var g [4096]byte
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ const msanenabled = true
|
|||
// the runtime, but operations like a slice copy can call msanread
|
||||
// anyhow for values on the stack. Just ignore msanread when running
|
||||
// on the system stack. The other msan functions are fine.
|
||||
//
|
||||
//go:nosplit
|
||||
func msanread(addr unsafe.Pointer, sz uintptr) {
|
||||
g := getg()
|
||||
if g == g.m.g0 || g == g.m.gsignal {
|
||||
if g == nil || g.m == nil || g == g.m.g0 || g == g.m.gsignal {
|
||||
return
|
||||
}
|
||||
domsanread(addr, sz)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package runtime_test
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
|
@ -67,7 +68,6 @@ func checkGdbPython(t *testing.T) {
|
|||
}
|
||||
|
||||
const helloSource = `
|
||||
package main
|
||||
import "fmt"
|
||||
var gslice []string
|
||||
func main() {
|
||||
|
|
@ -85,9 +85,20 @@ func main() {
|
|||
`
|
||||
|
||||
func TestGdbPython(t *testing.T) {
|
||||
testGdbPython(t, false)
|
||||
}
|
||||
|
||||
func TestGdbPythonCgo(t *testing.T) {
|
||||
testGdbPython(t, true)
|
||||
}
|
||||
|
||||
func testGdbPython(t *testing.T, cgo bool) {
|
||||
if runtime.GOARCH == "mips64" {
|
||||
testenv.SkipFlaky(t, 18173)
|
||||
}
|
||||
if cgo && !build.Default.CgoEnabled {
|
||||
t.Skip("skipping because cgo is not enabled")
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
checkGdbEnvironment(t)
|
||||
|
|
@ -100,8 +111,15 @@ func TestGdbPython(t *testing.T) {
|
|||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("package main\n")
|
||||
if cgo {
|
||||
buf.WriteString(`import "C"` + "\n")
|
||||
}
|
||||
buf.WriteString(helloSource)
|
||||
|
||||
src := filepath.Join(dir, "main.go")
|
||||
err = ioutil.WriteFile(src, []byte(helloSource), 0644)
|
||||
err = ioutil.WriteFile(src, buf.Bytes(), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create file: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,6 +285,25 @@ func modulesinit() {
|
|||
md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss)
|
||||
}
|
||||
}
|
||||
|
||||
// Modules appear in the moduledata linked list in the order they are
|
||||
// loaded by the dynamic loader, with one exception: the
|
||||
// firstmoduledata itself the module that contains the runtime. This
|
||||
// is not always the first module (when using -buildmode=shared, it
|
||||
// is typically libstd.so, the second module). The order matters for
|
||||
// typelinksinit, so we swap the first module with whatever module
|
||||
// contains the main function.
|
||||
//
|
||||
// See Issue #18729.
|
||||
mainText := funcPC(main_main)
|
||||
for i, md := range *modules {
|
||||
if md.text <= mainText && mainText <= md.etext {
|
||||
(*modules)[0] = md
|
||||
(*modules)[i] = &firstmoduledata
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
atomicstorep(unsafe.Pointer(&modulesSlice), unsafe.Pointer(modules))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// run
|
||||
|
||||
// Copyright 2017 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 main
|
||||
|
||||
import "os"
|
||||
|
||||
func panicWhenNot(cond bool) {
|
||||
if cond {
|
||||
os.Exit(0)
|
||||
} else {
|
||||
panic("nilcheck elim failed")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
e := (*string)(nil)
|
||||
panicWhenNot(e == e)
|
||||
// Should never reach this line.
|
||||
panicWhenNot(*e == *e)
|
||||
}
|
||||
|
|
@ -40,23 +40,23 @@ var (
|
|||
)
|
||||
|
||||
func f1() {
|
||||
_ = *intp // ERROR "generated nil check"
|
||||
_ = *intp // ERROR "removed nil check"
|
||||
|
||||
// This one should be removed but the block copy needs
|
||||
// to be turned into its own pseudo-op in order to see
|
||||
// the indirect.
|
||||
_ = *arrayp // ERROR "generated nil check"
|
||||
_ = *arrayp // ERROR "removed nil check"
|
||||
|
||||
// 0-byte indirect doesn't suffice.
|
||||
// we don't registerize globals, so there are no removed.* nil checks.
|
||||
_ = *array0p // ERROR "generated nil check"
|
||||
_ = *array0p // ERROR "removed nil check"
|
||||
_ = *array0p // ERROR "generated nil check"
|
||||
|
||||
_ = *intp // ERROR "removed nil check"
|
||||
_ = *intp // ERROR "generated nil check"
|
||||
_ = *arrayp // ERROR "removed nil check"
|
||||
_ = *structp // ERROR "generated nil check"
|
||||
_ = *emptyp // ERROR "generated nil check"
|
||||
_ = *arrayp // ERROR "removed nil check"
|
||||
_ = *arrayp // ERROR "generated nil check"
|
||||
}
|
||||
|
||||
func f2() {
|
||||
|
|
@ -71,15 +71,15 @@ func f2() {
|
|||
empty1p *Empty1
|
||||
)
|
||||
|
||||
_ = *intp // ERROR "generated nil check"
|
||||
_ = *arrayp // ERROR "generated nil check"
|
||||
_ = *array0p // ERROR "generated nil check"
|
||||
_ = *array0p // ERROR "removed.* nil check"
|
||||
_ = *intp // ERROR "removed.* nil check"
|
||||
_ = *arrayp // ERROR "removed.* nil check"
|
||||
_ = *array0p // ERROR "removed.* nil check"
|
||||
_ = *array0p // ERROR "generated nil check"
|
||||
_ = *intp // ERROR "generated nil check"
|
||||
_ = *arrayp // ERROR "removed.* nil check"
|
||||
_ = *structp // ERROR "generated nil check"
|
||||
_ = *emptyp // ERROR "generated nil check"
|
||||
_ = *arrayp // ERROR "removed.* nil check"
|
||||
_ = *arrayp // ERROR "generated nil check"
|
||||
_ = *bigarrayp // ERROR "generated nil check" ARM removed nil check before indirect!!
|
||||
_ = *bigstructp // ERROR "generated nil check"
|
||||
_ = *empty1p // ERROR "generated nil check"
|
||||
|
|
@ -122,16 +122,16 @@ func f3(x *[10000]int) {
|
|||
// x wasn't going to change across the function call.
|
||||
// But it's a little complex to do and in practice doesn't
|
||||
// matter enough.
|
||||
_ = x[9999] // ERROR "removed nil check"
|
||||
_ = x[9999] // ERROR "generated nil check" // TODO: fix
|
||||
}
|
||||
|
||||
func f3a() {
|
||||
x := fx10k()
|
||||
y := fx10k()
|
||||
z := fx10k()
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
y = z
|
||||
_ = &x[9] // ERROR "removed.* nil check"
|
||||
y = z
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
x = y
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
}
|
||||
|
|
@ -139,11 +139,11 @@ func f3a() {
|
|||
func f3b() {
|
||||
x := fx10k()
|
||||
y := fx10k()
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
_ = &x[9] // ERROR "removed.* nil check"
|
||||
y = x
|
||||
_ = &x[9] // ERROR "removed.* nil check"
|
||||
x = y
|
||||
_ = &x[9] // ERROR "removed.* nil check"
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
}
|
||||
|
||||
func fx10() *[10]int
|
||||
|
|
@ -179,15 +179,15 @@ func f4(x *[10]int) {
|
|||
_ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
|
||||
|
||||
fx10()
|
||||
_ = x[9] // ERROR "removed nil check"
|
||||
_ = x[9] // ERROR "generated nil check" // TODO: fix
|
||||
|
||||
x = fx10()
|
||||
y := fx10()
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
_ = &x[9] // ERROR "removed[a-z ]* nil check"
|
||||
y = x
|
||||
_ = &x[9] // ERROR "removed[a-z ]* nil check"
|
||||
x = y
|
||||
_ = &x[9] // ERROR "removed[a-z ]* nil check"
|
||||
_ = &x[9] // ERROR "generated nil check"
|
||||
}
|
||||
|
||||
func f5(p *float32, q *float64, r *float32, s *float64) float64 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue