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  }