go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/runtime/goroutine/goroutine_id.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Duplicated from stdlib net/http2/gotrack.go, because the Go Authors know 6 // what's best for us, and this is the only portable way to get the goroutine 7 // ID, even though this number is readily available in the go internals as 8 // a real integer. 9 10 package goroutine 11 12 import ( 13 "bytes" 14 "errors" 15 "fmt" 16 "runtime" 17 "strconv" 18 "sync" 19 ) 20 21 // ID is the ID number of a goroutine. 22 type ID uint64 23 24 var goroutineSpace = []byte("goroutine ") 25 26 // CurID gets the ID number of the current goroutine. 27 // 28 // According to the Go Authors, using this number will cause, among other 29 // things: spontaneous combustion, incurable insanity, and rapid acute 30 // cardiac lithomophosis (RACL). 31 // 32 // That said, it turns out that this functionality is very important for 33 // implementing things such as the errors.Annotate functionality. 34 // 35 // If, at some point, the Go Authors deign to provide this functionality via the 36 // runtime package, this method and type will be deleted immediately in favor of 37 // that. 38 // 39 // (if you are a Go Author, please, please, PLEASE provide this as part of the 40 // stdlib...). 41 func CurID() ID { 42 bp := littleBuf.Get().(*[]byte) 43 defer littleBuf.Put(bp) 44 b := *bp 45 b = b[:runtime.Stack(b, false)] 46 // Parse the 4707 out of "goroutine 4707 [" 47 b = bytes.TrimPrefix(b, goroutineSpace) 48 i := bytes.IndexByte(b, ' ') 49 if i < 0 { 50 panic(fmt.Sprintf("No space found in %q", b)) 51 } 52 b = b[:i] 53 n, err := parseUintBytes(b, 10, 64) 54 if err != nil { 55 panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) 56 } 57 return ID(n) 58 } 59 60 var littleBuf = sync.Pool{ 61 New: func() any { 62 buf := make([]byte, 64) 63 return &buf 64 }, 65 } 66 67 // parseUintBytes is like strconv.ParseUint, but using a []byte. 68 func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { 69 var cutoff, maxVal uint64 70 71 if bitSize == 0 { 72 bitSize = int(strconv.IntSize) 73 } 74 75 s0 := s 76 switch { 77 case len(s) < 1: 78 err = strconv.ErrSyntax 79 goto Error 80 81 case 2 <= base && base <= 36: 82 // valid base; nothing to do 83 84 case base == 0: 85 // Look for octal, hex prefix. 86 switch { 87 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 88 base = 16 89 s = s[2:] 90 if len(s) < 1 { 91 err = strconv.ErrSyntax 92 goto Error 93 } 94 case s[0] == '0': 95 base = 8 96 default: 97 base = 10 98 } 99 100 default: 101 err = errors.New("invalid base " + strconv.Itoa(base)) 102 goto Error 103 } 104 105 n = 0 106 cutoff = cutoff64(base) 107 maxVal = 1<<uint(bitSize) - 1 108 109 for i := 0; i < len(s); i++ { 110 var v byte 111 d := s[i] 112 switch { 113 case '0' <= d && d <= '9': 114 v = d - '0' 115 case 'a' <= d && d <= 'z': 116 v = d - 'a' + 10 117 case 'A' <= d && d <= 'Z': 118 v = d - 'A' + 10 119 default: 120 n = 0 121 err = strconv.ErrSyntax 122 goto Error 123 } 124 if int(v) >= base { 125 n = 0 126 err = strconv.ErrSyntax 127 goto Error 128 } 129 130 if n >= cutoff { 131 // n*base overflows 132 n = 1<<64 - 1 133 err = strconv.ErrRange 134 goto Error 135 } 136 n *= uint64(base) 137 138 n1 := n + uint64(v) 139 if n1 < n || n1 > maxVal { 140 // n+v overflows 141 n = 1<<64 - 1 142 err = strconv.ErrRange 143 goto Error 144 } 145 n = n1 146 } 147 148 return n, nil 149 150 Error: 151 return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} 152 } 153 154 // Return the first number n such that n*base >= 1<<64. 155 func cutoff64(base int) uint64 { 156 if base < 2 { 157 return 0 158 } 159 return (1<<64-1)/uint64(base) + 1 160 }