diff options
author | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 01:46:27 +0100 |
---|---|---|
committer | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 18:14:16 +0100 |
commit | 18f52af5be3a9a0c73811706108f790d65ee9c67 (patch) | |
tree | e13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/github.com/jtolds | |
parent | ebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff) |
[pkg] update vendor
Diffstat (limited to 'vendor/github.com/jtolds')
-rw-r--r-- | vendor/github.com/jtolds/gls/LICENSE | 18 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/README.md | 89 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/context.go | 153 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/gen_sym.go | 21 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/gid.go | 25 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/id_pool.go | 34 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/stack_tags.go | 147 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/stack_tags_js.go | 75 | ||||
-rw-r--r-- | vendor/github.com/jtolds/gls/stack_tags_main.go | 30 |
9 files changed, 592 insertions, 0 deletions
diff --git a/vendor/github.com/jtolds/gls/LICENSE b/vendor/github.com/jtolds/gls/LICENSE new file mode 100644 index 0000000..9b4a822 --- /dev/null +++ b/vendor/github.com/jtolds/gls/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2013, Space Monkey, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/jtolds/gls/README.md b/vendor/github.com/jtolds/gls/README.md new file mode 100644 index 0000000..4ebb692 --- /dev/null +++ b/vendor/github.com/jtolds/gls/README.md @@ -0,0 +1,89 @@ +gls +=== + +Goroutine local storage + +### IMPORTANT NOTE ### + +It is my duty to point you to https://blog.golang.org/context, which is how +Google solves all of the problems you'd perhaps consider using this package +for at scale. + +One downside to Google's approach is that *all* of your functions must have +a new first argument, but after clearing that hurdle everything else is much +better. + +If you aren't interested in this warning, read on. + +### Huhwaht? Why? ### + +Every so often, a thread shows up on the +[golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some +form of goroutine-local-storage, or some kind of goroutine id, or some kind of +context. There are a few valid use cases for goroutine-local-storage, one of +the most prominent being log line context. One poster was interested in being +able to log an HTTP request context id in every log line in the same goroutine +as the incoming HTTP request, without having to change every library and +function call he was interested in logging. + +This would be pretty useful. Provided that you could get some kind of +goroutine-local-storage, you could call +[log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging +writer that checks goroutine-local-storage for some context information and +adds that context to your log lines. + +But alas, Andrew Gerrand's typically diplomatic answer to the question of +goroutine-local variables was: + +> We wouldn't even be having this discussion if thread local storage wasn't +> useful. But every feature comes at a cost, and in my opinion the cost of +> threadlocals far outweighs their benefits. They're just not a good fit for +> Go. + +So, yeah, that makes sense. That's a pretty good reason for why the language +won't support a specific and (relatively) unuseful feature that requires some +runtime changes, just for the sake of a little bit of log improvement. + +But does Go require runtime changes? + +### How it works ### + +Go has pretty fantastic introspective and reflective features, but one thing Go +doesn't give you is any kind of access to the stack pointer, or frame pointer, +or goroutine id, or anything contextual about your current stack. It gives you +access to your list of callers, but only along with program counters, which are +fixed at compile time. + +But it does give you the stack. + +So, we define 16 special functions and embed base-16 tags into the stack using +the call order of those 16 functions. Then, we can read our tags back out of +the stack looking at the callers list. + +We then use these tags as an index into a traditional map for implementing +this library. + +### What are people saying? ### + +"Wow, that's horrifying." + +"This is the most terrible thing I have seen in a very long time." + +"Where is it getting a context from? Is this serializing all the requests? +What the heck is the client being bound to? What are these tags? Why does he +need callers? Oh god no. No no no." + +### Docs ### + +Please see the docs at http://godoc.org/github.com/jtolds/gls + +### Related ### + +If you're okay relying on the string format of the current runtime stacktrace +including a unique goroutine id (not guaranteed by the spec or anything, but +very unlikely to change within a Go release), you might be able to squeeze +out a bit more performance by using this similar library, inspired by some +code Brad Fitzpatrick wrote for debugging his HTTP/2 library: +https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require +any knowledge of the string format of the runtime stacktrace, which +probably adds unnecessary overhead). diff --git a/vendor/github.com/jtolds/gls/context.go b/vendor/github.com/jtolds/gls/context.go new file mode 100644 index 0000000..618a171 --- /dev/null +++ b/vendor/github.com/jtolds/gls/context.go @@ -0,0 +1,153 @@ +// Package gls implements goroutine-local storage. +package gls + +import ( + "sync" +) + +var ( + mgrRegistry = make(map[*ContextManager]bool) + mgrRegistryMtx sync.RWMutex +) + +// Values is simply a map of key types to value types. Used by SetValues to +// set multiple values at once. +type Values map[interface{}]interface{} + +// ContextManager is the main entrypoint for interacting with +// Goroutine-local-storage. You can have multiple independent ContextManagers +// at any given time. ContextManagers are usually declared globally for a given +// class of context variables. You should use NewContextManager for +// construction. +type ContextManager struct { + mtx sync.Mutex + values map[uint]Values +} + +// NewContextManager returns a brand new ContextManager. It also registers the +// new ContextManager in the ContextManager registry which is used by the Go +// method. ContextManagers are typically defined globally at package scope. +func NewContextManager() *ContextManager { + mgr := &ContextManager{values: make(map[uint]Values)} + mgrRegistryMtx.Lock() + defer mgrRegistryMtx.Unlock() + mgrRegistry[mgr] = true + return mgr +} + +// Unregister removes a ContextManager from the global registry, used by the +// Go method. Only intended for use when you're completely done with a +// ContextManager. Use of Unregister at all is rare. +func (m *ContextManager) Unregister() { + mgrRegistryMtx.Lock() + defer mgrRegistryMtx.Unlock() + delete(mgrRegistry, m) +} + +// SetValues takes a collection of values and a function to call for those +// values to be set in. Anything further down the stack will have the set +// values available through GetValue. SetValues will add new values or replace +// existing values of the same key and will not mutate or change values for +// previous stack frames. +// SetValues is slow (makes a copy of all current and new values for the new +// gls-context) in order to reduce the amount of lookups GetValue requires. +func (m *ContextManager) SetValues(new_values Values, context_call func()) { + if len(new_values) == 0 { + context_call() + return + } + + mutated_keys := make([]interface{}, 0, len(new_values)) + mutated_vals := make(Values, len(new_values)) + + EnsureGoroutineId(func(gid uint) { + m.mtx.Lock() + state, found := m.values[gid] + if !found { + state = make(Values, len(new_values)) + m.values[gid] = state + } + m.mtx.Unlock() + + for key, new_val := range new_values { + mutated_keys = append(mutated_keys, key) + if old_val, ok := state[key]; ok { + mutated_vals[key] = old_val + } + state[key] = new_val + } + + defer func() { + if !found { + m.mtx.Lock() + delete(m.values, gid) + m.mtx.Unlock() + return + } + + for _, key := range mutated_keys { + if val, ok := mutated_vals[key]; ok { + state[key] = val + } else { + delete(state, key) + } + } + }() + + context_call() + }) +} + +// GetValue will return a previously set value, provided that the value was set +// by SetValues somewhere higher up the stack. If the value is not found, ok +// will be false. +func (m *ContextManager) GetValue(key interface{}) ( + value interface{}, ok bool) { + gid, ok := GetGoroutineId() + if !ok { + return nil, false + } + + m.mtx.Lock() + state, found := m.values[gid] + m.mtx.Unlock() + + if !found { + return nil, false + } + value, ok = state[key] + return value, ok +} + +func (m *ContextManager) getValues() Values { + gid, ok := GetGoroutineId() + if !ok { + return nil + } + m.mtx.Lock() + state, _ := m.values[gid] + m.mtx.Unlock() + return state +} + +// Go preserves ContextManager values and Goroutine-local-storage across new +// goroutine invocations. The Go method makes a copy of all existing values on +// all registered context managers and makes sure they are still set after +// kicking off the provided function in a new goroutine. If you don't use this +// Go method instead of the standard 'go' keyword, you will lose values in +// ContextManagers, as goroutines have brand new stacks. +func Go(cb func()) { + mgrRegistryMtx.RLock() + defer mgrRegistryMtx.RUnlock() + + for mgr := range mgrRegistry { + values := mgr.getValues() + if len(values) > 0 { + cb = func(mgr *ContextManager, cb func()) func() { + return func() { mgr.SetValues(values, cb) } + }(mgr, cb) + } + } + + go cb() +} diff --git a/vendor/github.com/jtolds/gls/gen_sym.go b/vendor/github.com/jtolds/gls/gen_sym.go new file mode 100644 index 0000000..7f615cc --- /dev/null +++ b/vendor/github.com/jtolds/gls/gen_sym.go @@ -0,0 +1,21 @@ +package gls + +import ( + "sync" +) + +var ( + keyMtx sync.Mutex + keyCounter uint64 +) + +// ContextKey is a throwaway value you can use as a key to a ContextManager +type ContextKey struct{ id uint64 } + +// GenSym will return a brand new, never-before-used ContextKey +func GenSym() ContextKey { + keyMtx.Lock() + defer keyMtx.Unlock() + keyCounter += 1 + return ContextKey{id: keyCounter} +} diff --git a/vendor/github.com/jtolds/gls/gid.go b/vendor/github.com/jtolds/gls/gid.go new file mode 100644 index 0000000..c16bf3a --- /dev/null +++ b/vendor/github.com/jtolds/gls/gid.go @@ -0,0 +1,25 @@ +package gls + +var ( + stackTagPool = &idPool{} +) + +// Will return this goroutine's identifier if set. If you always need a +// goroutine identifier, you should use EnsureGoroutineId which will make one +// if there isn't one already. +func GetGoroutineId() (gid uint, ok bool) { + return readStackTag() +} + +// Will call cb with the current goroutine identifier. If one hasn't already +// been generated, one will be created and set first. The goroutine identifier +// might be invalid after cb returns. +func EnsureGoroutineId(cb func(gid uint)) { + if gid, ok := readStackTag(); ok { + cb(gid) + return + } + gid := stackTagPool.Acquire() + defer stackTagPool.Release(gid) + addStackTag(gid, func() { cb(gid) }) +} diff --git a/vendor/github.com/jtolds/gls/id_pool.go b/vendor/github.com/jtolds/gls/id_pool.go new file mode 100644 index 0000000..b7974ae --- /dev/null +++ b/vendor/github.com/jtolds/gls/id_pool.go @@ -0,0 +1,34 @@ +package gls + +// though this could probably be better at keeping ids smaller, the goal of +// this class is to keep a registry of the smallest unique integer ids +// per-process possible + +import ( + "sync" +) + +type idPool struct { + mtx sync.Mutex + released []uint + max_id uint +} + +func (p *idPool) Acquire() (id uint) { + p.mtx.Lock() + defer p.mtx.Unlock() + if len(p.released) > 0 { + id = p.released[len(p.released)-1] + p.released = p.released[:len(p.released)-1] + return id + } + id = p.max_id + p.max_id++ + return id +} + +func (p *idPool) Release(id uint) { + p.mtx.Lock() + defer p.mtx.Unlock() + p.released = append(p.released, id) +} diff --git a/vendor/github.com/jtolds/gls/stack_tags.go b/vendor/github.com/jtolds/gls/stack_tags.go new file mode 100644 index 0000000..37bbd33 --- /dev/null +++ b/vendor/github.com/jtolds/gls/stack_tags.go @@ -0,0 +1,147 @@ +package gls + +// so, basically, we're going to encode integer tags in base-16 on the stack + +const ( + bitWidth = 4 + stackBatchSize = 16 +) + +var ( + pc_lookup = make(map[uintptr]int8, 17) + mark_lookup [16]func(uint, func()) +) + +func init() { + setEntries := func(f func(uint, func()), v int8) { + var ptr uintptr + f(0, func() { + ptr = findPtr() + }) + pc_lookup[ptr] = v + if v >= 0 { + mark_lookup[v] = f + } + } + setEntries(github_com_jtolds_gls_markS, -0x1) + setEntries(github_com_jtolds_gls_mark0, 0x0) + setEntries(github_com_jtolds_gls_mark1, 0x1) + setEntries(github_com_jtolds_gls_mark2, 0x2) + setEntries(github_com_jtolds_gls_mark3, 0x3) + setEntries(github_com_jtolds_gls_mark4, 0x4) + setEntries(github_com_jtolds_gls_mark5, 0x5) + setEntries(github_com_jtolds_gls_mark6, 0x6) + setEntries(github_com_jtolds_gls_mark7, 0x7) + setEntries(github_com_jtolds_gls_mark8, 0x8) + setEntries(github_com_jtolds_gls_mark9, 0x9) + setEntries(github_com_jtolds_gls_markA, 0xa) + setEntries(github_com_jtolds_gls_markB, 0xb) + setEntries(github_com_jtolds_gls_markC, 0xc) + setEntries(github_com_jtolds_gls_markD, 0xd) + setEntries(github_com_jtolds_gls_markE, 0xe) + setEntries(github_com_jtolds_gls_markF, 0xf) +} + +func addStackTag(tag uint, context_call func()) { + if context_call == nil { + return + } + github_com_jtolds_gls_markS(tag, context_call) +} + +// these private methods are named this horrendous name so gopherjs support +// is easier. it shouldn't add any runtime cost in non-js builds. + +//go:noinline +func github_com_jtolds_gls_markS(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark0(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark1(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark2(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark3(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark4(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark5(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark6(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark7(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark8(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_mark9(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_markA(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_markB(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_markC(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_markD(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_markE(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline +func github_com_jtolds_gls_markF(tag uint, cb func()) { _m(tag, cb) } + +func _m(tag_remainder uint, cb func()) { + if tag_remainder == 0 { + cb() + } else { + mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb) + } +} + +func readStackTag() (tag uint, ok bool) { + var current_tag uint + offset := 0 + for { + batch, next_offset := getStack(offset, stackBatchSize) + for _, pc := range batch { + val, ok := pc_lookup[pc] + if !ok { + continue + } + if val < 0 { + return current_tag, true + } + current_tag <<= bitWidth + current_tag += uint(val) + } + if next_offset == 0 { + break + } + offset = next_offset + } + return 0, false +} + +func (m *ContextManager) preventInlining() { + // dunno if findPtr or getStack are likely to get inlined in a future release + // of go, but if they are inlined and their callers are inlined, that could + // hork some things. let's do our best to explain to the compiler that we + // really don't want those two functions inlined by saying they could change + // at any time. assumes preventInlining doesn't get compiled out. + // this whole thing is probably overkill. + findPtr = m.values[0][0].(func() uintptr) + getStack = m.values[0][1].(func(int, int) ([]uintptr, int)) +} diff --git a/vendor/github.com/jtolds/gls/stack_tags_js.go b/vendor/github.com/jtolds/gls/stack_tags_js.go new file mode 100644 index 0000000..c4e8b80 --- /dev/null +++ b/vendor/github.com/jtolds/gls/stack_tags_js.go @@ -0,0 +1,75 @@ +// +build js + +package gls + +// This file is used for GopherJS builds, which don't have normal runtime +// stack trace support + +import ( + "strconv" + "strings" + + "github.com/gopherjs/gopherjs/js" +) + +const ( + jsFuncNamePrefix = "github_com_jtolds_gls_mark" +) + +func jsMarkStack() (f []uintptr) { + lines := strings.Split( + js.Global.Get("Error").New().Get("stack").String(), "\n") + f = make([]uintptr, 0, len(lines)) + for i, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + if i == 0 { + if line != "Error" { + panic("didn't understand js stack trace") + } + continue + } + fields := strings.Fields(line) + if len(fields) < 2 || fields[0] != "at" { + panic("didn't understand js stack trace") + } + + pos := strings.Index(fields[1], jsFuncNamePrefix) + if pos < 0 { + continue + } + pos += len(jsFuncNamePrefix) + if pos >= len(fields[1]) { + panic("didn't understand js stack trace") + } + char := string(fields[1][pos]) + switch char { + case "S": + f = append(f, uintptr(0)) + default: + val, err := strconv.ParseUint(char, 16, 8) + if err != nil { + panic("didn't understand js stack trace") + } + f = append(f, uintptr(val)+1) + } + } + return f +} + +// variables to prevent inlining +var ( + findPtr = func() uintptr { + funcs := jsMarkStack() + if len(funcs) == 0 { + panic("failed to find function pointer") + } + return funcs[0] + } + + getStack = func(offset, amount int) (stack []uintptr, next_offset int) { + return jsMarkStack(), 0 + } +) diff --git a/vendor/github.com/jtolds/gls/stack_tags_main.go b/vendor/github.com/jtolds/gls/stack_tags_main.go new file mode 100644 index 0000000..4da89e4 --- /dev/null +++ b/vendor/github.com/jtolds/gls/stack_tags_main.go @@ -0,0 +1,30 @@ +// +build !js + +package gls + +// This file is used for standard Go builds, which have the expected runtime +// support + +import ( + "runtime" +) + +var ( + findPtr = func() uintptr { + var pc [1]uintptr + n := runtime.Callers(4, pc[:]) + if n != 1 { + panic("failed to find function pointer") + } + return pc[0] + } + + getStack = func(offset, amount int) (stack []uintptr, next_offset int) { + stack = make([]uintptr, amount) + stack = stack[:runtime.Callers(offset, stack)] + if len(stack) < amount { + return stack, 0 + } + return stack, offset + len(stack) + } +) |