diff options
author | Chris Broadfoot <cbro@golang.org> | 2016-08-08 11:28:55 -0700 |
---|---|---|
committer | Chris Broadfoot <cbro@golang.org> | 2016-08-08 11:30:51 -0700 |
commit | 78d024a5880a3a1b6e779a6585344b6c20a200ec (patch) | |
tree | 3f988c90d5a622c30347ab8e50b33880fc69bad5 | |
parent | 09fc3cc5df6b37b62a219bd4cacd8898a2328b76 (diff) | |
parent | 7a622740655bb5fcbd160eb96887032314842e6e (diff) | |
download | go-78d024a5880a3a1b6e779a6585344b6c20a200ec.tar.gz go-78d024a5880a3a1b6e779a6585344b6c20a200ec.zip |
all: merge master into release-branch.go1.7
7a62274 net/http: make Transport use new connection if over HTTP/2 concurrency limit
219ca60 doc: fix required OS X version inconsistency for binary downloads
26015b9 runtime: make stack 16-byte aligned for external code in _rt0_amd64_linux_lib
9fde86b runtime, syscall: fix kernel gettimeofday ABI change on iOS 10
3a03e87 os: check for waitid returning ENOSYS
1031675 net/http: update bundled http2 for flow control window adjustment fix
da070be syscall: fix Gettimeofday on macOS Sierra
f135c32 runtime: initialize hash algs before typemap
Change-Id: Ie176f3db1e253d75ae8e56b16d3fd9900b37dde3
-rw-r--r-- | doc/install.html | 4 | ||||
-rw-r--r-- | src/net/http/h2_bundle.go | 112 | ||||
-rw-r--r-- | src/net/http/transport.go | 9 | ||||
-rw-r--r-- | src/os/wait_waitid.go | 6 | ||||
-rw-r--r-- | src/runtime/alg.go | 2 | ||||
-rw-r--r-- | src/runtime/proc.go | 3 | ||||
-rw-r--r-- | src/runtime/rt0_linux_amd64.s | 8 | ||||
-rw-r--r-- | src/runtime/sys_darwin_arm.s | 15 | ||||
-rw-r--r-- | src/runtime/sys_darwin_arm64.s | 14 | ||||
-rw-r--r-- | src/syscall/syscall_darwin_386.go | 21 | ||||
-rw-r--r-- | src/syscall/syscall_darwin_amd64.go | 21 | ||||
-rw-r--r-- | src/syscall/syscall_darwin_arm.go | 13 | ||||
-rw-r--r-- | src/syscall/syscall_darwin_arm64.go | 13 | ||||
-rw-r--r-- | src/syscall/syscall_darwin_test.go | 23 |
14 files changed, 198 insertions, 66 deletions
diff --git a/doc/install.html b/doc/install.html index 0e6b86fdaf..cfe3e67a64 100644 --- a/doc/install.html +++ b/doc/install.html @@ -17,7 +17,7 @@ <p> <a href="https://golang.org/dl/" target="_blank">Official binary distributions</a> are available for the FreeBSD (release 8-STABLE and above), -Linux, Mac OS X (10.7 and above), and Windows operating systems and +Linux, Mac OS X (10.8 and above), and Windows operating systems and the 32-bit (<code>386</code>) and 64-bit (<code>amd64</code>) x86 processor architectures. </p> @@ -49,7 +49,7 @@ If your OS or architecture is not on the list, you may be able to <tr><td colspan="3"><hr></td></tr> <tr><td>FreeBSD 8-STABLE or later</td> <td>amd64</td> <td>Debian GNU/kFreeBSD not supported</td></tr> <tr><td>Linux 2.6.23 or later with glibc</td> <td>amd64, 386, arm</td> <td>CentOS/RHEL 5.x not supported</td></tr> -<tr><td>Mac OS X 10.7 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup></td></tr> +<tr><td>Mac OS X 10.7 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr> <tr><td>Windows XP or later</td> <td>amd64, 386</td> <td>use MinGW gcc<sup>†</sup>. No need for cygwin or msys.</td></tr> </table> diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index cd66c0960e..ffe15f0605 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -28,6 +28,7 @@ import ( "io" "io/ioutil" "log" + "math" "net" "net/http/httptrace" "net/textproto" @@ -403,9 +404,17 @@ func (e http2ConnectionError) Error() string { type http2StreamError struct { StreamID uint32 Code http2ErrCode + Cause error // optional additional detail +} + +func http2streamError(id uint32, code http2ErrCode) http2StreamError { + return http2StreamError{StreamID: id, Code: code} } func (e http2StreamError) Error() string { + if e.Cause != nil { + return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause) + } return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code) } @@ -1366,7 +1375,7 @@ func http2parseWindowUpdateFrame(fh http2FrameHeader, p []byte) (http2Frame, err if fh.StreamID == 0 { return nil, http2ConnectionError(http2ErrCodeProtocol) } - return nil, http2StreamError{fh.StreamID, http2ErrCodeProtocol} + return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol) } return &http2WindowUpdateFrame{ http2FrameHeader: fh, @@ -1444,7 +1453,7 @@ func http2parseHeadersFrame(fh http2FrameHeader, p []byte) (_ http2Frame, err er } } if len(p)-int(padLength) <= 0 { - return nil, http2StreamError{fh.StreamID, http2ErrCodeProtocol} + return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol) } hf.headerFragBuf = p[:len(p)-int(padLength)] return hf, nil @@ -1911,6 +1920,9 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr hdec.SetEmitEnabled(true) hdec.SetMaxStringLength(fr.maxHeaderStringLen()) hdec.SetEmitFunc(func(hf hpack.HeaderField) { + if http2VerboseLogs && http2logFrameReads { + log.Printf("http2: decoded hpack field %+v", hf) + } if !httplex.ValidHeaderFieldValue(hf.Value) { invalid = http2headerFieldValueError(hf.Value) } @@ -1969,11 +1981,17 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr } if invalid != nil { fr.errDetail = invalid - return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol} + if http2VerboseLogs { + log.Printf("http2: invalid header: %v", invalid) + } + return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol, invalid} } if err := mh.checkPseudos(); err != nil { fr.errDetail = err - return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol} + if http2VerboseLogs { + log.Printf("http2: invalid pseudo headers: %v", err) + } + return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol, err} } return mh, nil } @@ -3604,7 +3622,7 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) { case http2stateOpen: st.state = http2stateHalfClosedLocal - errCancel := http2StreamError{st.id, http2ErrCodeCancel} + errCancel := http2streamError(st.id, http2ErrCodeCancel) sc.resetStream(errCancel) case http2stateHalfClosedRemote: sc.closeStream(st, http2errHandlerComplete) @@ -3797,7 +3815,7 @@ func (sc *http2serverConn) processWindowUpdate(f *http2WindowUpdateFrame) error return nil } if !st.flow.add(int32(f.Increment)) { - return http2StreamError{f.StreamID, http2ErrCodeFlowControl} + return http2streamError(f.StreamID, http2ErrCodeFlowControl) } default: if !sc.flow.add(int32(f.Increment)) { @@ -3819,7 +3837,7 @@ func (sc *http2serverConn) processResetStream(f *http2RSTStreamFrame) error { if st != nil { st.gotReset = true st.cancelCtx() - sc.closeStream(st, http2StreamError{f.StreamID, f.ErrCode}) + sc.closeStream(st, http2streamError(f.StreamID, f.ErrCode)) } return nil } @@ -3922,13 +3940,13 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { if !ok || st.state != http2stateOpen || st.gotTrailerHeader { if sc.inflow.available() < int32(f.Length) { - return http2StreamError{id, http2ErrCodeFlowControl} + return http2streamError(id, http2ErrCodeFlowControl) } sc.inflow.take(int32(f.Length)) sc.sendWindowUpdate(nil, int(f.Length)) - return http2StreamError{id, http2ErrCodeStreamClosed} + return http2streamError(id, http2ErrCodeStreamClosed) } if st.body == nil { panic("internal error: should have a body in this state") @@ -3936,19 +3954,19 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes { st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes)) - return http2StreamError{id, http2ErrCodeStreamClosed} + return http2streamError(id, http2ErrCodeStreamClosed) } if f.Length > 0 { if st.inflow.available() < int32(f.Length) { - return http2StreamError{id, http2ErrCodeFlowControl} + return http2streamError(id, http2ErrCodeFlowControl) } st.inflow.take(int32(f.Length)) if len(data) > 0 { wrote, err := st.body.Write(data) if err != nil { - return http2StreamError{id, http2ErrCodeStreamClosed} + return http2streamError(id, http2ErrCodeStreamClosed) } if wrote != len(data) { panic("internal error: bad Writer") @@ -4046,10 +4064,10 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { if sc.unackedSettings == 0 { - return http2StreamError{st.id, http2ErrCodeProtocol} + return http2streamError(st.id, http2ErrCodeProtocol) } - return http2StreamError{st.id, http2ErrCodeRefusedStream} + return http2streamError(st.id, http2ErrCodeRefusedStream) } rw, req, err := sc.newWriterAndRequest(st, f) @@ -4083,18 +4101,18 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { } st.gotTrailerHeader = true if !f.StreamEnded() { - return http2StreamError{st.id, http2ErrCodeProtocol} + return http2streamError(st.id, http2ErrCodeProtocol) } if len(f.PseudoFields()) > 0 { - return http2StreamError{st.id, http2ErrCodeProtocol} + return http2streamError(st.id, http2ErrCodeProtocol) } if st.trailer != nil { for _, hf := range f.RegularFields() { key := sc.canonicalHeader(hf.Name) if !http2ValidTrailerHeader(key) { - return http2StreamError{st.id, http2ErrCodeProtocol} + return http2streamError(st.id, http2ErrCodeProtocol) } st.trailer[key] = append(st.trailer[key], hf.Value) } @@ -4148,18 +4166,18 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead isConnect := method == "CONNECT" if isConnect { if path != "" || scheme != "" || authority == "" { - return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol} + return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) } } else if method == "" || path == "" || (scheme != "https" && scheme != "http") { - return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol} + return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) } bodyOpen := !f.StreamEnded() if method == "HEAD" && bodyOpen { - return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol} + return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) } var tlsState *tls.ConnectionState // nil if not scheme https @@ -4216,7 +4234,7 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead var err error url_, err = url.ParseRequestURI(path) if err != nil { - return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol} + return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) } requestURI = path } @@ -4993,14 +5011,14 @@ type http2ClientConn struct { br *bufio.Reader fr *http2Framer lastActive time.Time - - // Settings from peer: + // Settings from peer: (also guarded by mu) maxFrameSize uint32 maxConcurrentStreams uint32 initialWindowSize uint32 - hbuf bytes.Buffer // HPACK encoder writes into this - henc *hpack.Encoder - freeBuf [][]byte + + hbuf bytes.Buffer // HPACK encoder writes into this + henc *hpack.Encoder + freeBuf [][]byte wmu sync.Mutex // held while writing; acquire AFTER mu if holding both werr error // first write error that has occurred @@ -5244,10 +5262,6 @@ func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) { } func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2ClientConn, error) { - if http2VerboseLogs { - t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr()) - } - cc := &http2ClientConn{ t: t, tconn: c, @@ -5260,6 +5274,10 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client singleUse: singleUse, wantSettingsAck: true, } + if http2VerboseLogs { + t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) + } + cc.cond = sync.NewCond(&cc.mu) cc.flow.add(int32(http2initialWindowSize)) @@ -5324,7 +5342,7 @@ func (cc *http2ClientConn) canTakeNewRequestLocked() bool { } return cc.goAway == nil && !cc.closed && int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) && - cc.nextStreamID < 2147483647 + cc.nextStreamID < math.MaxInt32 } func (cc *http2ClientConn) closeIfIdle() { @@ -5334,9 +5352,13 @@ func (cc *http2ClientConn) closeIfIdle() { return } cc.closed = true + nextID := cc.nextStreamID cc.mu.Unlock() + if http2VerboseLogs { + cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2) + } cc.tconn.Close() } @@ -5986,11 +6008,15 @@ func (rl *http2clientConnReadLoop) run() error { for { f, err := cc.fr.ReadFrame() if err != nil { - cc.vlogf("Transport readFrame error: (%T) %v", err, err) + cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) } if se, ok := err.(http2StreamError); ok { if cs := cc.streamByID(se.StreamID, true); cs != nil { - rl.endStreamError(cs, cc.fr.errDetail) + cs.cc.writeStreamReset(cs.ID, se.Code, err) + if se.Cause == nil { + se.Cause = cc.fr.errDetail + } + rl.endStreamError(cs, se) } continue } else if err != nil { @@ -6034,6 +6060,9 @@ func (rl *http2clientConnReadLoop) run() error { cc.logf("Transport: unhandled response frame type %T", f) } if err != nil { + if http2VerboseLogs { + cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, http2summarizeFrame(f), err) + } return err } if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 { @@ -6381,6 +6410,11 @@ func (rl *http2clientConnReadLoop) endStreamError(cs *http2clientStream, err err if http2isConnectionCloseRequest(cs.req) { rl.closeWhenIdle = true } + + select { + case cs.resc <- http2resAndError{err: err}: + default: + } } func (cs *http2clientStream) copyTrailers() { @@ -6425,6 +6459,16 @@ func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error cc.maxConcurrentStreams = s.Val case http2SettingInitialWindowSize: + if s.Val > math.MaxInt32 { + return http2ConnectionError(http2ErrCodeFlowControl) + } + + delta := int32(s.Val) - int32(cc.initialWindowSize) + for _, cs := range cc.streams { + cs.flow.add(delta) + } + cc.cond.Broadcast() + cc.initialWindowSize = s.Val default: @@ -6475,7 +6519,7 @@ func (rl *http2clientConnReadLoop) processResetStream(f *http2RSTStreamFrame) er case <-cs.peerReset: default: - err := http2StreamError{cs.ID, f.ErrCode} + err := http2streamError(cs.ID, f.ErrCode) cs.resetErr = err close(cs.peerReset) cs.bufPipe.CloseWithError(err) diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 009f3c5b6a..3046de5a8e 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -398,6 +398,15 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { // HTTP request on a new connection. The non-nil input error is the // error from roundTrip. func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool { + if err == http2ErrNoCachedConn { + // Issue 16582: if the user started a bunch of + // requests at once, they can all pick the same conn + // and violate the server's max concurrent streams. + // Instead, match the HTTP/1 behavior for now and dial + // again to get a new TCP connection, rather than failing + // this request. + return true + } if err == errMissingHost { // User error. return false diff --git a/src/os/wait_waitid.go b/src/os/wait_waitid.go index 5dbd7f9766..74b7494c0d 100644 --- a/src/os/wait_waitid.go +++ b/src/os/wait_waitid.go @@ -28,6 +28,12 @@ func (p *Process) blockUntilWaitable() (bool, error) { _, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0) runtime.KeepAlive(psig) if e != 0 { + // waitid has been available since Linux 2.6.9, but + // reportedly is not available in Ubuntu on Windows. + // See issue 16610. + if e == syscall.ENOSYS { + return false, nil + } return false, NewSyscallError("waitid", e) } return true, nil diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 66943495b5..147332e1fd 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -289,7 +289,7 @@ var aeskeysched [hashRandomBytes]byte // used in hash{32,64}.go to seed the hash function var hashkey [4]uintptr -func init() { +func alginit() { // Install aes hash algorithm if we have the instructions we need if (GOARCH == "386" || GOARCH == "amd64") && GOOS != "nacl" && diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 1d00930ac5..e693f7e05f 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -439,7 +439,8 @@ func schedinit() { stackinit() mallocinit() mcommoninit(_g_.m) - typelinksinit() + alginit() // maps must not be used before this call + typelinksinit() // uses maps itabsinit() msigsave(_g_.m) diff --git a/src/runtime/rt0_linux_amd64.s b/src/runtime/rt0_linux_amd64.s index 564b51c0b3..ced471f5cb 100644 --- a/src/runtime/rt0_linux_amd64.s +++ b/src/runtime/rt0_linux_amd64.s @@ -12,13 +12,18 @@ TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8 // When building with -buildmode=c-shared, this symbol is called when the shared // library is loaded. -TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0x48 +// Note: This function calls external C code, which might required 16-byte stack +// alignment after cmd/internal/obj applies its transformations. +TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0x50 + MOVQ SP, AX + ANDQ $-16, SP MOVQ BX, 0x10(SP) MOVQ BP, 0x18(SP) MOVQ R12, 0x20(SP) MOVQ R13, 0x28(SP) MOVQ R14, 0x30(SP) MOVQ R15, 0x38(SP) + MOVQ AX, 0x40(SP) MOVQ DI, _rt0_amd64_linux_lib_argc<>(SB) MOVQ SI, _rt0_amd64_linux_lib_argv<>(SB) @@ -50,6 +55,7 @@ restore: MOVQ 0x28(SP), R13 MOVQ 0x30(SP), R14 MOVQ 0x38(SP), R15 + MOVQ 0x40(SP), SP RET TEXT _rt0_amd64_linux_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/sys_darwin_arm.s b/src/runtime/sys_darwin_arm.s index 6b6437dddd..52f6a94d46 100644 --- a/src/runtime/sys_darwin_arm.s +++ b/src/runtime/sys_darwin_arm.s @@ -162,11 +162,15 @@ TEXT runtime·mincore(SB),NOSPLIT,$0 TEXT time·now(SB), 7, $32 MOVW $8(R13), R0 // timeval MOVW $0, R1 // zone + MOVW $0, R2 // see issue 16570 MOVW $SYS_gettimeofday, R12 SWI $0x80 // Note: R0 is tv_sec, R1 is tv_usec - + CMP $0, R0 + BNE inreg + MOVW 8(R13), R0 + MOVW 12(R13), R1 +inreg: MOVW R1, R2 // usec - MOVW R0, sec+0(FP) MOVW $0, R1 MOVW R1, loc+4(FP) @@ -178,9 +182,14 @@ TEXT time·now(SB), 7, $32 TEXT runtime·nanotime(SB),NOSPLIT,$32 MOVW $8(R13), R0 // timeval MOVW $0, R1 // zone + MOVW $0, R2 // see issue 16570 MOVW $SYS_gettimeofday, R12 SWI $0x80 // Note: R0 is tv_sec, R1 is tv_usec - + CMP $0, R0 + BNE inreg + MOVW 8(R13), R0 + MOVW 12(R13), R1 +inreg: MOVW R1, R2 MOVW $1000000000, R3 MULLU R0, R3, (R1, R0) diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s index a3b851d2fc..8e6b5b1ebf 100644 --- a/src/runtime/sys_darwin_arm64.s +++ b/src/runtime/sys_darwin_arm64.s @@ -155,9 +155,14 @@ TEXT time·now(SB),NOSPLIT,$40-12 MOVD RSP, R0 // timeval MOVD R0, R9 // this is how dyld calls gettimeofday MOVW $0, R1 // zone + MOVD $0, R2 // see issue 16570 MOVW $SYS_gettimeofday, R16 SVC $0x80 // Note: x0 is tv_sec, w1 is tv_usec - + CMP $0, R0 + BNE inreg + MOVD 0(RSP), R0 + MOVW 8(RSP), R1 +inreg: MOVD R0, sec+0(FP) MOVW $1000, R3 MUL R3, R1 @@ -168,9 +173,14 @@ TEXT runtime·nanotime(SB),NOSPLIT,$40 MOVD RSP, R0 // timeval MOVD R0, R9 // this is how dyld calls gettimeofday MOVW $0, R1 // zone + MOVD $0, R2 // see issue 16570 MOVW $SYS_gettimeofday, R16 SVC $0x80 // Note: x0 is tv_sec, w1 is tv_usec - + CMP $0, R0 + BNE inreg + MOVD 0(RSP), R0 + MOVW 8(RSP), R1 +inreg: MOVW $1000000000, R3 MUL R3, R0 MOVW $1000, R3 diff --git a/src/syscall/syscall_darwin_386.go b/src/syscall/syscall_darwin_386.go index 7dbb1c3d64..f75de000bd 100644 --- a/src/syscall/syscall_darwin_386.go +++ b/src/syscall/syscall_darwin_386.go @@ -26,14 +26,21 @@ func NsecToTimeval(nsec int64) (tv Timeval) { } //sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error) -func Gettimeofday(tv *Timeval) (err error) { - // The tv passed to gettimeofday must be non-nil - // but is otherwise unused. The answers come back - // in the two registers. +func Gettimeofday(tv *Timeval) error { + // The tv passed to gettimeofday must be non-nil. + // Before macOS Sierra (10.12), tv was otherwise unused and + // the answers came back in the two registers. + // As of Sierra, gettimeofday return zeros and populates + // tv itself. sec, usec, err := gettimeofday(tv) - tv.Sec = int32(sec) - tv.Usec = int32(usec) - return err + if err != nil { + return err + } + if sec != 0 || usec != 0 { + tv.Sec = int32(sec) + tv.Usec = int32(usec) + } + return nil } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/src/syscall/syscall_darwin_amd64.go b/src/syscall/syscall_darwin_amd64.go index 80e6024aeb..79083117b6 100644 --- a/src/syscall/syscall_darwin_amd64.go +++ b/src/syscall/syscall_darwin_amd64.go @@ -26,14 +26,21 @@ func NsecToTimeval(nsec int64) (tv Timeval) { } //sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, err error) -func Gettimeofday(tv *Timeval) (err error) { - // The tv passed to gettimeofday must be non-nil - // but is otherwise unused. The answers come back - // in the two registers. +func Gettimeofday(tv *Timeval) error { + // The tv passed to gettimeofday must be non-nil. + // Before macOS Sierra (10.12), tv was otherwise unused and + // the answers came back in the two registers. + // As of Sierra, gettimeofday return zeros and populates + // tv itself. sec, usec, err := gettimeofday(tv) - tv.Sec = sec - tv.Usec = usec - return err + if err != nil { + return err + } + if sec != 0 || usec != 0 { + tv.Sec = sec + tv.Usec = usec + } + return nil } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/src/syscall/syscall_darwin_arm.go b/src/syscall/syscall_darwin_arm.go index c302d83131..fe431039f4 100644 --- a/src/syscall/syscall_darwin_arm.go +++ b/src/syscall/syscall_darwin_arm.go @@ -26,14 +26,19 @@ func NsecToTimeval(nsec int64) (tv Timeval) { } //sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error) -func Gettimeofday(tv *Timeval) (err error) { +func Gettimeofday(tv *Timeval) error { // The tv passed to gettimeofday must be non-nil // but is otherwise unused. The answers come back // in the two registers. sec, usec, err := gettimeofday(tv) - tv.Sec = int32(sec) - tv.Usec = int32(usec) - return err + if err != nil { + return err + } + if sec != 0 || usec != 0 { + tv.Sec = int32(sec) + tv.Usec = int32(usec) + } + return nil } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/src/syscall/syscall_darwin_arm64.go b/src/syscall/syscall_darwin_arm64.go index 29f40d4229..d396e25332 100644 --- a/src/syscall/syscall_darwin_arm64.go +++ b/src/syscall/syscall_darwin_arm64.go @@ -26,14 +26,19 @@ func NsecToTimeval(nsec int64) (tv Timeval) { } //sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, err error) -func Gettimeofday(tv *Timeval) (err error) { +func Gettimeofday(tv *Timeval) error { // The tv passed to gettimeofday must be non-nil // but is otherwise unused. The answers come back // in the two registers. sec, usec, err := gettimeofday(tv) - tv.Sec = sec - tv.Usec = usec - return err + if err != nil { + return err + } + if sec != 0 || usec != 0 { + tv.Sec = sec + tv.Usec = usec + } + return nil } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/src/syscall/syscall_darwin_test.go b/src/syscall/syscall_darwin_test.go new file mode 100644 index 0000000000..cea5636d07 --- /dev/null +++ b/src/syscall/syscall_darwin_test.go @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// +build darwin +// +build amd64 386 arm arm64 + +package syscall_test + +import ( + "syscall" + "testing" +) + +func TestDarwinGettimeofday(t *testing.T) { + tv := &syscall.Timeval{} + if err := syscall.Gettimeofday(tv); err != nil { + t.Fatal(err) + } + if tv.Sec == 0 && tv.Usec == 0 { + t.Fatal("Sec and Usec both zero") + } +} |