| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | // 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" | 
					
						
							| 
									
										
										
										
											2025-09-17 14:48:09 +02:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-17 14:48:09 +02:00
										 |  |  | // Setting DebugGoroutines to false during a test to disable goroutine debugging | 
					
						
							|  |  |  | // results in race detector complaints when a test leaves goroutines running before | 
					
						
							|  |  |  | // returning. Tests shouldn't do this, of course, but when they do it generally shows | 
					
						
							|  |  |  | // up as infrequent, hard-to-debug flakes. (See #66519.) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Disable goroutine debugging during individual tests with an atomic bool. | 
					
						
							|  |  |  | // (Note that it's safe to enable/disable debugging mid-test, so the actual race condition | 
					
						
							|  |  |  | // here is harmless.) | 
					
						
							|  |  |  | var disableDebugGoroutines atomic.Bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | type goroutineLock uint64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newGoroutineLock() goroutineLock { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:48:09 +02:00
										 |  |  | 	if !DebugGoroutines || disableDebugGoroutines.Load() { | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | 		return 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return goroutineLock(curGoroutineID()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (g goroutineLock) check() { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:48:09 +02:00
										 |  |  | 	if !DebugGoroutines || disableDebugGoroutines.Load() { | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if curGoroutineID() != uint64(g) { | 
					
						
							|  |  |  | 		panic("running on the wrong goroutine") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (g goroutineLock) checkNotOn() { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:48:09 +02:00
										 |  |  | 	if !DebugGoroutines || disableDebugGoroutines.Load() { | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | 		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 | 
					
						
							|  |  |  | } |