]> Cypherpunks.ru repositories - gostls13.git/blob - src/internal/godebug/godebug.go
679e3df8d69bda9ad8e36df7d97c10bcc12747e1
[gostls13.git] / src / internal / godebug / godebug.go
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 //
24 // Each time a non-default setting causes a change in program behavior,
25 // code should call [Setting.IncNonDefault] to increment a counter that can
26 // be reported by [runtime/metrics.Read].
27 // Note that counters used with IncNonDefault must be added to
28 // various tables in other packages. See the [Setting.IncNonDefault]
29 // documentation for details.
30 package godebug
31
32 import (
33         "sync"
34         "sync/atomic"
35         _ "unsafe" // go:linkname
36 )
37
38 // A Setting is a single setting in the $GODEBUG environment variable.
39 type Setting struct {
40         name string
41         once sync.Once
42         *setting
43 }
44
45 type setting struct {
46         value          atomic.Pointer[string]
47         nonDefaultOnce sync.Once
48         nonDefault     atomic.Uint64
49 }
50
51 // New returns a new Setting for the $GODEBUG setting with the given name.
52 func New(name string) *Setting {
53         return &Setting{name: name}
54 }
55
56 // Name returns the name of the setting.
57 func (s *Setting) Name() string {
58         return s.name
59 }
60
61 // String returns a printable form for the setting: name=value.
62 func (s *Setting) String() string {
63         return s.name + "=" + s.Value()
64 }
65
66 // IncNonDefault increments the non-default behavior counter
67 // associated with the given setting.
68 // This counter is exposed in the runtime/metrics value
69 // /godebug/non-default-behavior/<name>:events.
70 //
71 // Note that Value must be called at least once before IncNonDefault.
72 //
73 // Any GODEBUG setting that can call IncNonDefault must be listed
74 // in three more places:
75 //
76 //   - the table in ../runtime/metrics.go (search for non-default-behavior)
77 //   - the table in ../../runtime/metrics/description.go (search for non-default-behavior; run 'go generate' afterward)
78 //   - the table in ../../cmd/go/internal/load/godebug.go (search for defaultGodebugs)
79 func (s *Setting) IncNonDefault() {
80         s.nonDefaultOnce.Do(s.register)
81         s.nonDefault.Add(1)
82 }
83
84 func (s *Setting) register() {
85         registerMetric("/godebug/non-default-behavior/"+s.name+":events", s.nonDefault.Load)
86 }
87
88 // cache is a cache of all the GODEBUG settings,
89 // a locked map[string]*atomic.Pointer[string].
90 //
91 // All Settings with the same name share a single
92 // *atomic.Pointer[string], so that when GODEBUG
93 // changes only that single atomic string pointer
94 // needs to be updated.
95 //
96 // A name appears in the values map either if it is the
97 // name of a Setting for which Value has been called
98 // at least once, or if the name has ever appeared in
99 // a name=value pair in the $GODEBUG environment variable.
100 // Once entered into the map, the name is never removed.
101 var cache sync.Map // name string -> value *atomic.Pointer[string]
102
103 var empty string
104
105 // Value returns the current value for the GODEBUG setting s.
106 //
107 // Value maintains an internal cache that is synchronized
108 // with changes to the $GODEBUG environment variable,
109 // making Value efficient to call as frequently as needed.
110 // Clients should therefore typically not attempt their own
111 // caching of Value's result.
112 func (s *Setting) Value() string {
113         s.once.Do(func() {
114                 s.setting = lookup(s.name)
115         })
116         return *s.value.Load()
117 }
118
119 // lookup returns the unique *setting value for the given name.
120 func lookup(name string) *setting {
121         if v, ok := cache.Load(name); ok {
122                 return v.(*setting)
123         }
124         s := new(setting)
125         s.value.Store(&empty)
126         if v, loaded := cache.LoadOrStore(name, s); loaded {
127                 // Lost race: someone else created it. Use theirs.
128                 return v.(*setting)
129         }
130
131         return s
132 }
133
134 // setUpdate is provided by package runtime.
135 // It calls update(def, env), where def is the default GODEBUG setting
136 // and env is the current value of the $GODEBUG environment variable.
137 // After that first call, the runtime calls update(def, env)
138 // again each time the environment variable changes
139 // (due to use of os.Setenv, for example).
140 //
141 //go:linkname setUpdate
142 func setUpdate(update func(string, string))
143
144 // registerMetric is provided by package runtime.
145 // It forwards registrations to runtime/metrics.
146 //
147 //go:linkname registerMetric
148 func registerMetric(name string, read func() uint64)
149
150 // setNewNonDefaultInc is provided by package runtime.
151 // The runtime can do
152 //
153 //      inc := newNonDefaultInc(name)
154 //
155 // instead of
156 //
157 //      inc := godebug.New(name).IncNonDefault
158 //
159 // since it cannot import godebug.
160 //
161 //go:linkname setNewIncNonDefault
162 func setNewIncNonDefault(newIncNonDefault func(string) func())
163
164 func init() {
165         setUpdate(update)
166         setNewIncNonDefault(newIncNonDefault)
167 }
168
169 func newIncNonDefault(name string) func() {
170         s := New(name)
171         s.Value()
172         return s.IncNonDefault
173 }
174
175 var updateMu sync.Mutex
176
177 // update records an updated GODEBUG setting.
178 // def is the default GODEBUG setting for the running binary,
179 // and env is the current value of the $GODEBUG environment variable.
180 func update(def, env string) {
181         updateMu.Lock()
182         defer updateMu.Unlock()
183
184         // Update all the cached values, creating new ones as needed.
185         // We parse the environment variable first, so that any settings it has
186         // are already locked in place (did[name] = true) before we consider
187         // the defaults.
188         did := make(map[string]bool)
189         parse(did, env)
190         parse(did, def)
191
192         // Clear any cached values that are no longer present.
193         cache.Range(func(name, s any) bool {
194                 if !did[name.(string)] {
195                         s.(*setting).value.Store(&empty)
196                 }
197                 return true
198         })
199 }
200
201 // parse parses the GODEBUG setting string s,
202 // which has the form k=v,k2=v2,k3=v3.
203 // Later settings override earlier ones.
204 // Parse only updates settings k=v for which did[k] = false.
205 // It also sets did[k] = true for settings that it updates.
206 func parse(did map[string]bool, s string) {
207         // Scan the string backward so that later settings are used
208         // and earlier settings are ignored.
209         // Note that a forward scan would cause cached values
210         // to temporarily use the ignored value before being
211         // updated to the "correct" one.
212         end := len(s)
213         eq := -1
214         for i := end - 1; i >= -1; i-- {
215                 if i == -1 || s[i] == ',' {
216                         if eq >= 0 {
217                                 name, value := s[i+1:eq], s[eq+1:end]
218                                 if !did[name] {
219                                         did[name] = true
220                                         lookup(name).value.Store(&value)
221                                 }
222                         }
223                         eq = -1
224                         end = i
225                 } else if s[i] == '=' {
226                         eq = i
227                 }
228         }
229 }