github.com/Kolosok86/http@v0.1.2/http2/gotrack.go (about)

     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  )
    19  
    20  var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
    21  
    22  type goroutineLock uint64
    23  
    24  func newGoroutineLock() goroutineLock {
    25  	if !DebugGoroutines {
    26  		return 0
    27  	}
    28  	return goroutineLock(curGoroutineID())
    29  }
    30  
    31  func (g goroutineLock) check() {
    32  	if !DebugGoroutines {
    33  		return
    34  	}
    35  	if curGoroutineID() != uint64(g) {
    36  		panic("running on the wrong goroutine")
    37  	}
    38  }
    39  
    40  func (g goroutineLock) checkNotOn() {
    41  	if !DebugGoroutines {
    42  		return
    43  	}
    44  	if curGoroutineID() == uint64(g) {
    45  		panic("running on the wrong goroutine")
    46  	}
    47  }
    48  
    49  var goroutineSpace = []byte("goroutine ")
    50  
    51  func curGoroutineID() uint64 {
    52  	bp := littleBuf.Get().(*[]byte)
    53  	defer littleBuf.Put(bp)
    54  	b := *bp
    55  	b = b[:runtime.Stack(b, false)]
    56  	// Parse the 4707 out of "goroutine 4707 ["
    57  	b = bytes.TrimPrefix(b, goroutineSpace)
    58  	i := bytes.IndexByte(b, ' ')
    59  	if i < 0 {
    60  		panic(fmt.Sprintf("No space found in %q", b))
    61  	}
    62  	b = b[:i]
    63  	n, err := parseUintBytes(b, 10, 64)
    64  	if err != nil {
    65  		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
    66  	}
    67  	return n
    68  }
    69  
    70  var littleBuf = sync.Pool{
    71  	New: func() interface{} {
    72  		buf := make([]byte, 64)
    73  		return &buf
    74  	},
    75  }
    76  
    77  // parseUintBytes is like strconv.ParseUint, but using a []byte.
    78  func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
    79  	var cutoff, maxVal uint64
    80  
    81  	if bitSize == 0 {
    82  		bitSize = int(strconv.IntSize)
    83  	}
    84  
    85  	s0 := s
    86  	switch {
    87  	case len(s) < 1:
    88  		err = strconv.ErrSyntax
    89  		goto Error
    90  
    91  	case 2 <= base && base <= 36:
    92  		// valid base; nothing to do
    93  
    94  	case base == 0:
    95  		// Look for octal, hex prefix.
    96  		switch {
    97  		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
    98  			base = 16
    99  			s = s[2:]
   100  			if len(s) < 1 {
   101  				err = strconv.ErrSyntax
   102  				goto Error
   103  			}
   104  		case s[0] == '0':
   105  			base = 8
   106  		default:
   107  			base = 10
   108  		}
   109  
   110  	default:
   111  		err = errors.New("invalid base " + strconv.Itoa(base))
   112  		goto Error
   113  	}
   114  
   115  	n = 0
   116  	cutoff = cutoff64(base)
   117  	maxVal = 1<<uint(bitSize) - 1
   118  
   119  	for i := 0; i < len(s); i++ {
   120  		var v byte
   121  		d := s[i]
   122  		switch {
   123  		case '0' <= d && d <= '9':
   124  			v = d - '0'
   125  		case 'a' <= d && d <= 'z':
   126  			v = d - 'a' + 10
   127  		case 'A' <= d && d <= 'Z':
   128  			v = d - 'A' + 10
   129  		default:
   130  			n = 0
   131  			err = strconv.ErrSyntax
   132  			goto Error
   133  		}
   134  		if int(v) >= base {
   135  			n = 0
   136  			err = strconv.ErrSyntax
   137  			goto Error
   138  		}
   139  
   140  		if n >= cutoff {
   141  			// n*base overflows
   142  			n = 1<<64 - 1
   143  			err = strconv.ErrRange
   144  			goto Error
   145  		}
   146  		n *= uint64(base)
   147  
   148  		n1 := n + uint64(v)
   149  		if n1 < n || n1 > maxVal {
   150  			// n+v overflows
   151  			n = 1<<64 - 1
   152  			err = strconv.ErrRange
   153  			goto Error
   154  		}
   155  		n = n1
   156  	}
   157  
   158  	return n, nil
   159  
   160  Error:
   161  	return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
   162  }
   163  
   164  // Return the first number n such that n*base >= 1<<64.
   165  func cutoff64(base int) uint64 {
   166  	if base < 2 {
   167  		return 0
   168  	}
   169  	return (1<<64-1)/uint64(base) + 1
   170  }