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