1
2
3
4
5
6
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
24
25
26
27
28
29
30
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
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
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
104
105 case base == 0:
106
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
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
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
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