|
|
// Copyright 2014 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.
// Defensive debug-only utility to track that functions run on the
// goroutine that they're supposed to.
package http2
import ( "bytes" "errors" "fmt" "os" "runtime" "strconv" "sync" )
var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
type goroutineLock uint64
func newGoroutineLock() goroutineLock { if !DebugGoroutines { return 0 } return goroutineLock(curGoroutineID()) }
func (g goroutineLock) check() { if !DebugGoroutines { return } if curGoroutineID() != uint64(g) { panic("running on the wrong goroutine") } }
func (g goroutineLock) checkNotOn() { if !DebugGoroutines { return } if curGoroutineID() == uint64(g) { panic("running on the wrong goroutine") } }
var goroutineSpace = []byte("goroutine ")
func curGoroutineID() uint64 { bp := littleBuf.Get().(*[]byte) defer littleBuf.Put(bp) b := *bp b = b[:runtime.Stack(b, false)] // Parse the 4707 out of "goroutine 4707 ["
b = bytes.TrimPrefix(b, goroutineSpace) i := bytes.IndexByte(b, ' ') if i < 0 { panic(fmt.Sprintf("No space found in %q", b)) } b = b[:i] n, err := parseUintBytes(b, 10, 64) if err != nil { panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) } return n }
var littleBuf = sync.Pool{ New: func() interface{} { buf := make([]byte, 64) return &buf }, }
// parseUintBytes is like strconv.ParseUint, but using a []byte.
func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { var cutoff, maxVal uint64
if bitSize == 0 { bitSize = int(strconv.IntSize) }
s0 := s switch { case len(s) < 1: err = strconv.ErrSyntax goto Error
case 2 <= base && base <= 36: // valid base; nothing to do
case base == 0: // Look for octal, hex prefix.
switch { case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): base = 16 s = s[2:] if len(s) < 1 { err = strconv.ErrSyntax goto Error } case s[0] == '0': base = 8 default: base = 10 }
default: err = errors.New("invalid base " + strconv.Itoa(base)) goto Error }
n = 0 cutoff = cutoff64(base) maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ { var v byte d := s[i] switch { case '0' <= d && d <= '9': v = d - '0' case 'a' <= d && d <= 'z': v = d - 'a' + 10 case 'A' <= d && d <= 'Z': v = d - 'A' + 10 default: n = 0 err = strconv.ErrSyntax goto Error } if int(v) >= base { n = 0 err = strconv.ErrSyntax goto Error }
if n >= cutoff { // n*base overflows
n = 1<<64 - 1 err = strconv.ErrRange goto Error } n *= uint64(base)
n1 := n + uint64(v) if n1 < n || n1 > maxVal { // n+v overflows
n = 1<<64 - 1 err = strconv.ErrRange goto Error } n = n1 }
return n, nil
Error: return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} }
// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 { if base < 2 { return 0 } return (1<<64-1)/uint64(base) + 1 }
|