mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-12-26 20:53:10 +00:00
acc333c40b
When GTS is running in a container runtime which has configured CPU or memory limits or under an init system that uses cgroups to impose CPU and memory limits the values the Go runtime sees for GOMAXPROCS and GOMEMLIMIT are still based on the host resources, not the cgroup. At least for the throttling middlewares which use GOMAXPROCS to configure their queue size, this can result in GTS running with values too big compared to the resources that will actuall be available to it. This introduces 2 dependencies which can pick up resource contraints from the current cgroup and tune the Go runtime accordingly. This should result in the different queues being appropriately sized and in general more predictable performance. These dependencies are a no-op on non-Linux systems or if running in a cgroup that doesn't set a limit on CPU or memory. The automatic tuning of GOMEMLIMIT can be disabled by either explicitly setting GOMEMLIMIT yourself or by setting AUTOMEMLIMIT=off. The automatic tuning of GOMAXPROCS can similarly be counteracted by setting GOMAXPROCS yourself.
286 lines
6.3 KiB
Go
286 lines
6.3 KiB
Go
package dbus
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io"
|
|
"reflect"
|
|
)
|
|
|
|
type decoder struct {
|
|
in io.Reader
|
|
order binary.ByteOrder
|
|
pos int
|
|
}
|
|
|
|
// newDecoder returns a new decoder that reads values from in. The input is
|
|
// expected to be in the given byte order.
|
|
func newDecoder(in io.Reader, order binary.ByteOrder) *decoder {
|
|
dec := new(decoder)
|
|
dec.in = in
|
|
dec.order = order
|
|
return dec
|
|
}
|
|
|
|
// align aligns the input to the given boundary and panics on error.
|
|
func (dec *decoder) align(n int) {
|
|
if dec.pos%n != 0 {
|
|
newpos := (dec.pos + n - 1) & ^(n - 1)
|
|
empty := make([]byte, newpos-dec.pos)
|
|
if _, err := io.ReadFull(dec.in, empty); err != nil {
|
|
panic(err)
|
|
}
|
|
dec.pos = newpos
|
|
}
|
|
}
|
|
|
|
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
|
|
func (dec *decoder) binread(v interface{}) {
|
|
if err := binary.Read(dec.in, dec.order, v); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
|
|
defer func() {
|
|
var ok bool
|
|
v := recover()
|
|
if err, ok = v.(error); ok {
|
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
|
err = FormatError("unexpected EOF")
|
|
}
|
|
}
|
|
}()
|
|
vs = make([]interface{}, 0)
|
|
s := sig.str
|
|
for s != "" {
|
|
err, rem := validSingle(s, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v := dec.decode(s[:len(s)-len(rem)], 0)
|
|
vs = append(vs, v)
|
|
s = rem
|
|
}
|
|
return vs, nil
|
|
}
|
|
|
|
func (dec *decoder) decode(s string, depth int) interface{} {
|
|
dec.align(alignment(typeFor(s)))
|
|
switch s[0] {
|
|
case 'y':
|
|
var b [1]byte
|
|
if _, err := dec.in.Read(b[:]); err != nil {
|
|
panic(err)
|
|
}
|
|
dec.pos++
|
|
return b[0]
|
|
case 'b':
|
|
i := dec.decode("u", depth).(uint32)
|
|
switch {
|
|
case i == 0:
|
|
return false
|
|
case i == 1:
|
|
return true
|
|
default:
|
|
panic(FormatError("invalid value for boolean"))
|
|
}
|
|
case 'n':
|
|
var i int16
|
|
dec.binread(&i)
|
|
dec.pos += 2
|
|
return i
|
|
case 'i':
|
|
var i int32
|
|
dec.binread(&i)
|
|
dec.pos += 4
|
|
return i
|
|
case 'x':
|
|
var i int64
|
|
dec.binread(&i)
|
|
dec.pos += 8
|
|
return i
|
|
case 'q':
|
|
var i uint16
|
|
dec.binread(&i)
|
|
dec.pos += 2
|
|
return i
|
|
case 'u':
|
|
var i uint32
|
|
dec.binread(&i)
|
|
dec.pos += 4
|
|
return i
|
|
case 't':
|
|
var i uint64
|
|
dec.binread(&i)
|
|
dec.pos += 8
|
|
return i
|
|
case 'd':
|
|
var f float64
|
|
dec.binread(&f)
|
|
dec.pos += 8
|
|
return f
|
|
case 's':
|
|
length := dec.decode("u", depth).(uint32)
|
|
b := make([]byte, int(length)+1)
|
|
if _, err := io.ReadFull(dec.in, b); err != nil {
|
|
panic(err)
|
|
}
|
|
dec.pos += int(length) + 1
|
|
return string(b[:len(b)-1])
|
|
case 'o':
|
|
return ObjectPath(dec.decode("s", depth).(string))
|
|
case 'g':
|
|
length := dec.decode("y", depth).(byte)
|
|
b := make([]byte, int(length)+1)
|
|
if _, err := io.ReadFull(dec.in, b); err != nil {
|
|
panic(err)
|
|
}
|
|
dec.pos += int(length) + 1
|
|
sig, err := ParseSignature(string(b[:len(b)-1]))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return sig
|
|
case 'v':
|
|
if depth >= 64 {
|
|
panic(FormatError("input exceeds container depth limit"))
|
|
}
|
|
var variant Variant
|
|
sig := dec.decode("g", depth).(Signature)
|
|
if len(sig.str) == 0 {
|
|
panic(FormatError("variant signature is empty"))
|
|
}
|
|
err, rem := validSingle(sig.str, 0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if rem != "" {
|
|
panic(FormatError("variant signature has multiple types"))
|
|
}
|
|
variant.sig = sig
|
|
variant.value = dec.decode(sig.str, depth+1)
|
|
return variant
|
|
case 'h':
|
|
return UnixFDIndex(dec.decode("u", depth).(uint32))
|
|
case 'a':
|
|
if len(s) > 1 && s[1] == '{' {
|
|
ksig := s[2:3]
|
|
vsig := s[3 : len(s)-1]
|
|
v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
|
|
if depth >= 63 {
|
|
panic(FormatError("input exceeds container depth limit"))
|
|
}
|
|
length := dec.decode("u", depth).(uint32)
|
|
// Even for empty maps, the correct padding must be included
|
|
dec.align(8)
|
|
spos := dec.pos
|
|
for dec.pos < spos+int(length) {
|
|
dec.align(8)
|
|
if !isKeyType(v.Type().Key()) {
|
|
panic(InvalidTypeError{v.Type()})
|
|
}
|
|
kv := dec.decode(ksig, depth+2)
|
|
vv := dec.decode(vsig, depth+2)
|
|
v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
|
}
|
|
return v.Interface()
|
|
}
|
|
if depth >= 64 {
|
|
panic(FormatError("input exceeds container depth limit"))
|
|
}
|
|
sig := s[1:]
|
|
length := dec.decode("u", depth).(uint32)
|
|
// capacity can be determined only for fixed-size element types
|
|
var capacity int
|
|
if s := sigByteSize(sig); s != 0 {
|
|
capacity = int(length) / s
|
|
}
|
|
v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity)
|
|
// Even for empty arrays, the correct padding must be included
|
|
align := alignment(typeFor(s[1:]))
|
|
if len(s) > 1 && s[1] == '(' {
|
|
//Special case for arrays of structs
|
|
//structs decode as a slice of interface{} values
|
|
//but the dbus alignment does not match this
|
|
align = 8
|
|
}
|
|
dec.align(align)
|
|
spos := dec.pos
|
|
for dec.pos < spos+int(length) {
|
|
ev := dec.decode(s[1:], depth+1)
|
|
v = reflect.Append(v, reflect.ValueOf(ev))
|
|
}
|
|
return v.Interface()
|
|
case '(':
|
|
if depth >= 64 {
|
|
panic(FormatError("input exceeds container depth limit"))
|
|
}
|
|
dec.align(8)
|
|
v := make([]interface{}, 0)
|
|
s = s[1 : len(s)-1]
|
|
for s != "" {
|
|
err, rem := validSingle(s, 0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ev := dec.decode(s[:len(s)-len(rem)], depth+1)
|
|
v = append(v, ev)
|
|
s = rem
|
|
}
|
|
return v
|
|
default:
|
|
panic(SignatureError{Sig: s})
|
|
}
|
|
}
|
|
|
|
// sigByteSize tries to calculates size of the given signature in bytes.
|
|
//
|
|
// It returns zero when it can't, for example when it contains non-fixed size
|
|
// types such as strings, maps and arrays that require reading of the transmitted
|
|
// data, for that we would need to implement the unread method for Decoder first.
|
|
func sigByteSize(sig string) int {
|
|
var total int
|
|
for offset := 0; offset < len(sig); {
|
|
switch sig[offset] {
|
|
case 'y':
|
|
total += 1
|
|
offset += 1
|
|
case 'n', 'q':
|
|
total += 2
|
|
offset += 1
|
|
case 'b', 'i', 'u', 'h':
|
|
total += 4
|
|
offset += 1
|
|
case 'x', 't', 'd':
|
|
total += 8
|
|
offset += 1
|
|
case '(':
|
|
i := 1
|
|
depth := 1
|
|
for i < len(sig[offset:]) && depth != 0 {
|
|
if sig[offset+i] == '(' {
|
|
depth++
|
|
} else if sig[offset+i] == ')' {
|
|
depth--
|
|
}
|
|
i++
|
|
}
|
|
s := sigByteSize(sig[offset+1 : offset+i-1])
|
|
if s == 0 {
|
|
return 0
|
|
}
|
|
total += s
|
|
offset += i
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
return total
|
|
}
|
|
|
|
// A FormatError is an error in the wire format.
|
|
type FormatError string
|
|
|
|
func (e FormatError) Error() string {
|
|
return "dbus: wire format error: " + string(e)
|
|
}
|