Source file src/net/http/internal/http2/gotrack.go

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Defensive debug-only utility to track that functions run on the
     6  // goroutine that they're supposed to.
     7  
     8  package http2
     9  
    10  import (
    11  	"bytes"
    12  	"errors"
    13  	"fmt"
    14  	"os"
    15  	"runtime"
    16  	"strconv"
    17  	"sync"
    18  	"sync/atomic"
    19  )
    20  
    21  var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
    22  
    23  // Setting DebugGoroutines to false during a test to disable goroutine debugging
    24  // results in race detector complaints when a test leaves goroutines running before
    25  // returning. Tests shouldn't do this, of course, but when they do it generally shows
    26  // up as infrequent, hard-to-debug flakes. (See #66519.)
    27  //
    28  // Disable goroutine debugging during individual tests with an atomic bool.
    29  // (Note that it's safe to enable/disable debugging mid-test, so the actual race condition
    30  // here is harmless.)
    31  var disableDebugGoroutines atomic.Bool
    32  
    33  type goroutineLock uint64
    34  
    35  func newGoroutineLock() goroutineLock {
    36  	if !DebugGoroutines || disableDebugGoroutines.Load() {
    37  		return 0
    38  	}
    39  	return goroutineLock(curGoroutineID())
    40  }
    41  
    42  func (g goroutineLock) check() {
    43  	if !DebugGoroutines || disableDebugGoroutines.Load() {
    44  		return
    45  	}
    46  	if curGoroutineID() != uint64(g) {
    47  		panic("running on the wrong goroutine")
    48  	}
    49  }
    50  
    51  func (g goroutineLock) checkNotOn() {
    52  	if !DebugGoroutines || disableDebugGoroutines.Load() {
    53  		return
    54  	}
    55  	if curGoroutineID() == uint64(g) {
    56  		panic("running on the wrong goroutine")
    57  	}
    58  }
    59  
    60  var goroutineSpace = []byte("goroutine ")
    61  
    62  func curGoroutineID() uint64 {
    63  	bp := littleBuf.Get().(*[]byte)
    64  	defer littleBuf.Put(bp)
    65  	b := *bp
    66  	b = b[:runtime.Stack(b, false)]
    67  	// Parse the 4707 out of "goroutine 4707 ["
    68  	b = bytes.TrimPrefix(b, goroutineSpace)
    69  	i := bytes.IndexByte(b, ' ')
    70  	if i < 0 {
    71  		panic(fmt.Sprintf("No space found in %q", b))
    72  	}
    73  	b = b[:i]
    74  	n, err := parseUintBytes(b, 10, 64)
    75  	if err != nil {
    76  		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
    77  	}
    78  	return n
    79  }
    80  
    81  var littleBuf = sync.Pool{
    82  	New: func() interface{} {
    83  		buf := make([]byte, 64)
    84  		return &buf
    85  	},
    86  }
    87  
    88  // parseUintBytes is like strconv.ParseUint, but using a []byte.
    89  func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
    90  	var cutoff, maxVal uint64
    91  
    92  	if bitSize == 0 {
    93  		bitSize = int(strconv.IntSize)
    94  	}
    95  
    96  	s0 := s
    97  	switch {
    98  	case len(s) < 1:
    99  		err = strconv.ErrSyntax
   100  		goto Error
   101  
   102  	case 2 <= base && base <= 36:
   103  		// valid base; nothing to do
   104  
   105  	case base == 0:
   106  		// Look for octal, hex prefix.
   107  		switch {
   108  		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
   109  			base = 16
   110  			s = s[2:]
   111  			if len(s) < 1 {
   112  				err = strconv.ErrSyntax
   113  				goto Error
   114  			}
   115  		case s[0] == '0':
   116  			base = 8
   117  		default:
   118  			base = 10
   119  		}
   120  
   121  	default:
   122  		err = errors.New("invalid base " + strconv.Itoa(base))
   123  		goto Error
   124  	}
   125  
   126  	n = 0
   127  	cutoff = cutoff64(base)
   128  	maxVal = 1<<uint(bitSize) - 1
   129  
   130  	for i := 0; i < len(s); i++ {
   131  		var v byte
   132  		d := s[i]
   133  		switch {
   134  		case '0' <= d && d <= '9':
   135  			v = d - '0'
   136  		case 'a' <= d && d <= 'z':
   137  			v = d - 'a' + 10
   138  		case 'A' <= d && d <= 'Z':
   139  			v = d - 'A' + 10
   140  		default:
   141  			n = 0
   142  			err = strconv.ErrSyntax
   143  			goto Error
   144  		}
   145  		if int(v) >= base {
   146  			n = 0
   147  			err = strconv.ErrSyntax
   148  			goto Error
   149  		}
   150  
   151  		if n >= cutoff {
   152  			// n*base overflows
   153  			n = 1<<64 - 1
   154  			err = strconv.ErrRange
   155  			goto Error
   156  		}
   157  		n *= uint64(base)
   158  
   159  		n1 := n + uint64(v)
   160  		if n1 < n || n1 > maxVal {
   161  			// n+v overflows
   162  			n = 1<<64 - 1
   163  			err = strconv.ErrRange
   164  			goto Error
   165  		}
   166  		n = n1
   167  	}
   168  
   169  	return n, nil
   170  
   171  Error:
   172  	return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
   173  }
   174  
   175  // Return the first number n such that n*base >= 1<<64.
   176  func cutoff64(base int) uint64 {
   177  	if base < 2 {
   178  		return 0
   179  	}
   180  	return (1<<64-1)/uint64(base) + 1
   181  }
   182  

View as plain text