]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
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 | } |