mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-02 16:52:25 -06: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
							 | 
						||
| 
								 | 
							
								}
							 |