forked from toolshed/abra
		
	
		
			
				
	
	
		
			171 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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.
 | |
| 
 | |
| // Defensive debug-only utility to track that functions run on the
 | |
| // goroutine that they're supposed to.
 | |
| 
 | |
| package http2
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"runtime"
 | |
| 	"strconv"
 | |
| 	"sync"
 | |
| )
 | |
| 
 | |
| var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
 | |
| 
 | |
| type goroutineLock uint64
 | |
| 
 | |
| func newGoroutineLock() goroutineLock {
 | |
| 	if !DebugGoroutines {
 | |
| 		return 0
 | |
| 	}
 | |
| 	return goroutineLock(curGoroutineID())
 | |
| }
 | |
| 
 | |
| func (g goroutineLock) check() {
 | |
| 	if !DebugGoroutines {
 | |
| 		return
 | |
| 	}
 | |
| 	if curGoroutineID() != uint64(g) {
 | |
| 		panic("running on the wrong goroutine")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (g goroutineLock) checkNotOn() {
 | |
| 	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 out 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
 | |
| }
 |