blob: 9933c9f8c7487b62324558e18c2c878cb5e363d7 [file] [log] [blame]
Don Newton98fd8812019-09-23 15:15:02 -04001// 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
8package http2
9
10import (
11 "bytes"
12 "errors"
13 "fmt"
14 "os"
15 "runtime"
16 "strconv"
17 "sync"
18)
19
20var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
21
22type goroutineLock uint64
23
24func newGoroutineLock() goroutineLock {
25 if !DebugGoroutines {
26 return 0
27 }
28 return goroutineLock(curGoroutineID())
29}
30
31func (g goroutineLock) check() {
32 if !DebugGoroutines {
33 return
34 }
35 if curGoroutineID() != uint64(g) {
36 panic("running on the wrong goroutine")
37 }
38}
39
40func (g goroutineLock) checkNotOn() {
41 if !DebugGoroutines {
42 return
43 }
44 if curGoroutineID() == uint64(g) {
45 panic("running on the wrong goroutine")
46 }
47}
48
49var goroutineSpace = []byte("goroutine ")
50
51func 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
70var 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.
78func 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
160Error:
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.
165func cutoff64(base int) uint64 {
166 if base < 2 {
167 return 0
168 }
169 return (1<<64-1)/uint64(base) + 1
170}