github.com/bir3/gocompiler@v0.3.205/src/internal/godebug/godebug.go (about)

     1  // Copyright 2021 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  // Package godebug makes the settings in the $GODEBUG environment variable
     6  // available to other packages. These settings are often used for compatibility
     7  // tweaks, when we need to change a default behavior but want to let users
     8  // opt back in to the original. For example GODEBUG=http2server=0 disables
     9  // HTTP/2 support in the net/http server.
    10  //
    11  // In typical usage, code should declare a Setting as a global
    12  // and then call Value each time the current setting value is needed:
    13  //
    14  //	var http2server = godebug.New("http2server")
    15  //
    16  //	func ServeConn(c net.Conn) {
    17  //		if http2server.Value() == "0" {
    18  //			disallow HTTP/2
    19  //			...
    20  //		}
    21  //		...
    22  //	}
    23  package godebug
    24  
    25  import (
    26  	"sync"
    27  	"sync/atomic"
    28  	_ "unsafe" // go:linkname
    29  )
    30  
    31  // A Setting is a single setting in the $GODEBUG environment variable.
    32  type Setting struct {
    33  	name  string
    34  	once  sync.Once
    35  	value *atomic.Pointer[string]
    36  }
    37  
    38  // New returns a new Setting for the $GODEBUG setting with the given name.
    39  func New(name string) *Setting {
    40  	return &Setting{name: name}
    41  }
    42  
    43  // Name returns the name of the setting.
    44  func (s *Setting) Name() string {
    45  	return s.name
    46  }
    47  
    48  // String returns a printable form for the setting: name=value.
    49  func (s *Setting) String() string {
    50  	return s.name + "=" + s.Value()
    51  }
    52  
    53  // cache is a cache of all the GODEBUG settings,
    54  // a locked map[string]*atomic.Pointer[string].
    55  //
    56  // All Settings with the same name share a single
    57  // *atomic.Pointer[string], so that when GODEBUG
    58  // changes only that single atomic string pointer
    59  // needs to be updated.
    60  //
    61  // A name appears in the values map either if it is the
    62  // name of a Setting for which Value has been called
    63  // at least once, or if the name has ever appeared in
    64  // a name=value pair in the $GODEBUG environment variable.
    65  // Once entered into the map, the name is never removed.
    66  var cache sync.Map // name string -> value *atomic.Pointer[string]
    67  
    68  var empty string
    69  
    70  // Value returns the current value for the GODEBUG setting s.
    71  //
    72  // Value maintains an internal cache that is synchronized
    73  // with changes to the $GODEBUG environment variable,
    74  // making Value efficient to call as frequently as needed.
    75  // Clients should therefore typically not attempt their own
    76  // caching of Value's result.
    77  func (s *Setting) Value() string {
    78  	s.once.Do(func() {
    79  		v, ok := cache.Load(s.name)
    80  		if !ok {
    81  			p := new(atomic.Pointer[string])
    82  			p.Store(&empty)
    83  			v, _ = cache.LoadOrStore(s.name, p)
    84  		}
    85  		s.value = v.(*atomic.Pointer[string])
    86  	})
    87  	return *s.value.Load()
    88  }
    89  
    90  // setUpdate is provided by package runtime.
    91  // It calls update(def, env), where def is the default GODEBUG setting
    92  // and env is the current value of the $GODEBUG environment variable.
    93  // After that first call, the runtime calls update(def, env)
    94  // again each time the environment variable changes
    95  // (due to use of os.Setenv, for example).
    96  //
    97  //go:linkname setUpdate internal/godebug.setUpdate
    98  func setUpdate(update func(string, string))
    99  
   100  func init() {
   101  	setUpdate(update)
   102  }
   103  
   104  var updateMu sync.Mutex
   105  
   106  // update records an updated GODEBUG setting.
   107  // def is the default GODEBUG setting for the running binary,
   108  // and env is the current value of the $GODEBUG environment variable.
   109  func update(def, env string) {
   110  	updateMu.Lock()
   111  	defer updateMu.Unlock()
   112  
   113  	// Update all the cached values, creating new ones as needed.
   114  	// We parse the environment variable first, so that any settings it has
   115  	// are already locked in place (did[name] = true) before we consider
   116  	// the defaults.
   117  	did := make(map[string]bool)
   118  	parse(did, env)
   119  	parse(did, def)
   120  
   121  	// Clear any cached values that are no longer present.
   122  	cache.Range(func(name, v any) bool {
   123  		if !did[name.(string)] {
   124  			v.(*atomic.Pointer[string]).Store(&empty)
   125  		}
   126  		return true
   127  	})
   128  }
   129  
   130  // parse parses the GODEBUG setting string s,
   131  // which has the form k=v,k2=v2,k3=v3.
   132  // Later settings override earlier ones.
   133  // Parse only updates settings k=v for which did[k] = false.
   134  // It also sets did[k] = true for settings that it updates.
   135  func parse(did map[string]bool, s string) {
   136  	// Scan the string backward so that later settings are used
   137  	// and earlier settings are ignored.
   138  	// Note that a forward scan would cause cached values
   139  	// to temporarily use the ignored value before being
   140  	// updated to the "correct" one.
   141  	end := len(s)
   142  	eq := -1
   143  	for i := end - 1; i >= -1; i-- {
   144  		if i == -1 || s[i] == ',' {
   145  			if eq >= 0 {
   146  				name, value := s[i+1:eq], s[eq+1:end]
   147  				if !did[name] {
   148  					did[name] = true
   149  					v, ok := cache.Load(name)
   150  					if !ok {
   151  						p := new(atomic.Pointer[string])
   152  						p.Store(&empty)
   153  						v, _ = cache.LoadOrStore(name, p)
   154  					}
   155  					v.(*atomic.Pointer[string]).Store(&value)
   156  				}
   157  			}
   158  			eq = -1
   159  			end = i
   160  		} else if s[i] == '=' {
   161  			eq = i
   162  		}
   163  	}
   164  }