mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 17:02:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			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 | ||
|  | } |