You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
3.1 KiB
170 lines
3.1 KiB
// 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 |
|
}
|
|
|