| // 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" | |
| "sync/atomic" | |
| ) | |
| var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" | |
| // Setting DebugGoroutines to false during a test to disable goroutine debugging | |
| // results in race detector complaints when a test leaves goroutines running before | |
| // returning. Tests shouldn't do this, of course, but when they do it generally shows | |
| // up as infrequent, hard-to-debug flakes. (See #66519.) | |
| // | |
| // Disable goroutine debugging during individual tests with an atomic bool. | |
| // (Note that it's safe to enable/disable debugging mid-test, so the actual race condition | |
| // here is harmless.) | |
| var disableDebugGoroutines atomic.Bool | |
| type goroutineLock uint64 | |
| func newGoroutineLock() goroutineLock { | |
| if !DebugGoroutines || disableDebugGoroutines.Load() { | |
| return 0 | |
| } | |
| return goroutineLock(curGoroutineID()) | |
| } | |
| func (g goroutineLock) check() { | |
| if !DebugGoroutines || disableDebugGoroutines.Load() { | |
| return | |
| } | |
| if curGoroutineID() != uint64(g) { | |
| panic("running on the wrong goroutine") | |
| } | |
| } | |
| func (g goroutineLock) checkNotOn() { | |
| if !DebugGoroutines || disableDebugGoroutines.Load() { | |
| 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 | |
| } |