-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Debug-only runtime tracking of funcs running on correct goroutines.
- Loading branch information
Showing
4 changed files
with
217 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// Copyright 2014 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS | ||
// Licensed under the same terms as Go itself: | ||
// https://code.google.com/p/go/source/browse/LICENSE | ||
|
||
// Defensive debug-only utility to track that functions run on the | ||
// goroutine that they're supposed to. | ||
|
||
package http2 | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"runtime" | ||
"strconv" | ||
"sync" | ||
) | ||
|
||
var DebugGoroutines = false | ||
|
||
type goroutineLock uint64 | ||
|
||
func newGoroutineLock() goroutineLock { | ||
return goroutineLock(curGoroutineID()) | ||
} | ||
|
||
func (g goroutineLock) check() { | ||
if !DebugGoroutines { | ||
return | ||
} | ||
if curGoroutineID() != uint64(g) { | ||
panic("running on the wrong goroutine") | ||
} | ||
} | ||
|
||
var goroutineSpace = []byte("goroutine ") | ||
|
||
func curGoroutineID() uint64 { | ||
bp := littleBuf.Get().(*[]byte) | ||
defer littleBuf.Put(bp) | ||
b := *bp | ||
b = b[:runtime.Stack(b, false)] | ||
// Parse the 4707 otu of "goroutine 4707 [" | ||
b = bytes.TrimPrefix(b, goroutineSpace) | ||
i := bytes.IndexByte(b, ' ') | ||
if i < 0 { | ||
panic(fmt.Sprintf("No space found in %q", b)) | ||
} | ||
b = b[:i] | ||
n, err := parseUintBytes(b, 10, 64) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) | ||
} | ||
return n | ||
} | ||
|
||
var littleBuf = sync.Pool{ | ||
New: func() interface{} { | ||
buf := make([]byte, 64) | ||
return &buf | ||
}, | ||
} | ||
|
||
// parseUintBytes is like strconv.ParseUint, but using a []byte. | ||
func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { | ||
var cutoff, maxVal uint64 | ||
|
||
if bitSize == 0 { | ||
bitSize = int(strconv.IntSize) | ||
} | ||
|
||
s0 := s | ||
switch { | ||
case len(s) < 1: | ||
err = strconv.ErrSyntax | ||
goto Error | ||
|
||
case 2 <= base && base <= 36: | ||
// valid base; nothing to do | ||
|
||
case base == 0: | ||
// Look for octal, hex prefix. | ||
switch { | ||
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): | ||
base = 16 | ||
s = s[2:] | ||
if len(s) < 1 { | ||
err = strconv.ErrSyntax | ||
goto Error | ||
} | ||
case s[0] == '0': | ||
base = 8 | ||
default: | ||
base = 10 | ||
} | ||
|
||
default: | ||
err = errors.New("invalid base " + strconv.Itoa(base)) | ||
goto Error | ||
} | ||
|
||
n = 0 | ||
cutoff = cutoff64(base) | ||
maxVal = 1<<uint(bitSize) - 1 | ||
|
||
for i := 0; i < len(s); i++ { | ||
var v byte | ||
d := s[i] | ||
switch { | ||
case '0' <= d && d <= '9': | ||
v = d - '0' | ||
case 'a' <= d && d <= 'z': | ||
v = d - 'a' + 10 | ||
case 'A' <= d && d <= 'Z': | ||
v = d - 'A' + 10 | ||
default: | ||
n = 0 | ||
err = strconv.ErrSyntax | ||
goto Error | ||
} | ||
if int(v) >= base { | ||
n = 0 | ||
err = strconv.ErrSyntax | ||
goto Error | ||
} | ||
|
||
if n >= cutoff { | ||
// n*base overflows | ||
n = 1<<64 - 1 | ||
err = strconv.ErrRange | ||
goto Error | ||
} | ||
n *= uint64(base) | ||
|
||
n1 := n + uint64(v) | ||
if n1 < n || n1 > maxVal { | ||
// n+v overflows | ||
n = 1<<64 - 1 | ||
err = strconv.ErrRange | ||
goto Error | ||
} | ||
n = n1 | ||
} | ||
|
||
return n, nil | ||
|
||
Error: | ||
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} | ||
} | ||
|
||
// Return the first number n such that n*base >= 1<<64. | ||
func cutoff64(base int) uint64 { | ||
if base < 2 { | ||
return 0 | ||
} | ||
return (1<<64-1)/uint64(base) + 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright 2014 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS | ||
// Licensed under the same terms as Go itself: | ||
// https://code.google.com/p/go/source/browse/LICENSE | ||
|
||
package http2 | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestGoroutineLock(t *testing.T) { | ||
DebugGoroutines = true | ||
g := newGoroutineLock() | ||
g.check() | ||
|
||
sawPanic := make(chan interface{}) | ||
go func() { | ||
defer func() { sawPanic <- recover() }() | ||
g.check() // should panic | ||
}() | ||
e := <-sawPanic | ||
if e == nil { | ||
t.Fatal("did not see panic from check in other goroutine") | ||
} | ||
if !strings.Contains(fmt.Sprint(e), "wrong goroutine") { | ||
t.Errorf("expected on see panic about running on the wrong goroutine; got %v", e) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters