mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-11 20:12:45 +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.
432 lines
11 KiB
Go
432 lines
11 KiB
Go
package dbus
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
byteType = reflect.TypeOf(byte(0))
|
|
boolType = reflect.TypeOf(false)
|
|
uint8Type = reflect.TypeOf(uint8(0))
|
|
int16Type = reflect.TypeOf(int16(0))
|
|
uint16Type = reflect.TypeOf(uint16(0))
|
|
intType = reflect.TypeOf(int(0))
|
|
uintType = reflect.TypeOf(uint(0))
|
|
int32Type = reflect.TypeOf(int32(0))
|
|
uint32Type = reflect.TypeOf(uint32(0))
|
|
int64Type = reflect.TypeOf(int64(0))
|
|
uint64Type = reflect.TypeOf(uint64(0))
|
|
float64Type = reflect.TypeOf(float64(0))
|
|
stringType = reflect.TypeOf("")
|
|
signatureType = reflect.TypeOf(Signature{""})
|
|
objectPathType = reflect.TypeOf(ObjectPath(""))
|
|
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
|
interfacesType = reflect.TypeOf([]interface{}{})
|
|
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
|
unixFDType = reflect.TypeOf(UnixFD(0))
|
|
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
|
errType = reflect.TypeOf((*error)(nil)).Elem()
|
|
)
|
|
|
|
// An InvalidTypeError signals that a value which cannot be represented in the
|
|
// D-Bus wire format was passed to a function.
|
|
type InvalidTypeError struct {
|
|
Type reflect.Type
|
|
}
|
|
|
|
func (e InvalidTypeError) Error() string {
|
|
return "dbus: invalid type " + e.Type.String()
|
|
}
|
|
|
|
// Store copies the values contained in src to dest, which must be a slice of
|
|
// pointers. It converts slices of interfaces from src to corresponding structs
|
|
// in dest. An error is returned if the lengths of src and dest or the types of
|
|
// their elements don't match.
|
|
func Store(src []interface{}, dest ...interface{}) error {
|
|
if len(src) != len(dest) {
|
|
return errors.New("dbus.Store: length mismatch")
|
|
}
|
|
|
|
for i := range src {
|
|
if err := storeInterfaces(src[i], dest[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func storeInterfaces(src, dest interface{}) error {
|
|
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
|
|
}
|
|
|
|
func store(dest, src reflect.Value) error {
|
|
if dest.Kind() == reflect.Ptr {
|
|
if dest.IsNil() {
|
|
dest.Set(reflect.New(dest.Type().Elem()))
|
|
}
|
|
return store(dest.Elem(), src)
|
|
}
|
|
switch src.Kind() {
|
|
case reflect.Slice:
|
|
return storeSlice(dest, src)
|
|
case reflect.Map:
|
|
return storeMap(dest, src)
|
|
default:
|
|
return storeBase(dest, src)
|
|
}
|
|
}
|
|
|
|
func storeBase(dest, src reflect.Value) error {
|
|
return setDest(dest, src)
|
|
}
|
|
|
|
func setDest(dest, src reflect.Value) error {
|
|
if !isVariant(src.Type()) && isVariant(dest.Type()) {
|
|
//special conversion for dbus.Variant
|
|
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
|
|
return nil
|
|
}
|
|
if isVariant(src.Type()) && !isVariant(dest.Type()) {
|
|
src = getVariantValue(src)
|
|
return store(dest, src)
|
|
}
|
|
if !src.Type().ConvertibleTo(dest.Type()) {
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: cannot convert %s to %s",
|
|
src.Type(), dest.Type())
|
|
}
|
|
dest.Set(src.Convert(dest.Type()))
|
|
return nil
|
|
}
|
|
|
|
func kindsAreCompatible(dest, src reflect.Type) bool {
|
|
switch {
|
|
case isVariant(dest):
|
|
return true
|
|
case dest.Kind() == reflect.Interface:
|
|
return true
|
|
default:
|
|
return dest.Kind() == src.Kind()
|
|
}
|
|
}
|
|
|
|
func isConvertibleTo(dest, src reflect.Type) bool {
|
|
switch {
|
|
case isVariant(dest):
|
|
return true
|
|
case dest.Kind() == reflect.Interface:
|
|
return true
|
|
case dest.Kind() == reflect.Slice:
|
|
return src.Kind() == reflect.Slice &&
|
|
isConvertibleTo(dest.Elem(), src.Elem())
|
|
case dest.Kind() == reflect.Struct:
|
|
return src == interfacesType
|
|
default:
|
|
return src.ConvertibleTo(dest)
|
|
}
|
|
}
|
|
|
|
func storeMap(dest, src reflect.Value) error {
|
|
switch {
|
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"map: cannot store a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
case isVariant(dest.Type()):
|
|
return storeMapIntoVariant(dest, src)
|
|
case dest.Kind() == reflect.Interface:
|
|
return storeMapIntoInterface(dest, src)
|
|
case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
|
|
isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
|
return storeMapIntoMap(dest, src)
|
|
default:
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"map: cannot convert a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
}
|
|
}
|
|
|
|
func storeMapIntoVariant(dest, src reflect.Value) error {
|
|
dv := reflect.MakeMap(src.Type())
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeMapIntoInterface(dest, src reflect.Value) error {
|
|
var dv reflect.Value
|
|
if isVariant(src.Type().Elem()) {
|
|
//Convert variants to interface{} recursively when converting
|
|
//to interface{}
|
|
dv = reflect.MakeMap(
|
|
reflect.MapOf(src.Type().Key(), interfaceType))
|
|
} else {
|
|
dv = reflect.MakeMap(src.Type())
|
|
}
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeMapIntoMap(dest, src reflect.Value) error {
|
|
if dest.IsNil() {
|
|
dest.Set(reflect.MakeMap(dest.Type()))
|
|
}
|
|
keys := src.MapKeys()
|
|
for _, key := range keys {
|
|
dkey := key.Convert(dest.Type().Key())
|
|
dval := reflect.New(dest.Type().Elem()).Elem()
|
|
err := store(dval, getVariantValue(src.MapIndex(key)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dest.SetMapIndex(dkey, dval)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func storeSlice(dest, src reflect.Value) error {
|
|
switch {
|
|
case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
|
|
//The decoder always decodes structs as slices of interface{}
|
|
return storeStruct(dest, src)
|
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"slice: cannot store a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
case isVariant(dest.Type()):
|
|
return storeSliceIntoVariant(dest, src)
|
|
case dest.Kind() == reflect.Interface:
|
|
return storeSliceIntoInterface(dest, src)
|
|
case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
|
return storeSliceIntoSlice(dest, src)
|
|
default:
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"slice: cannot convert a value of %s into %s",
|
|
src.Type(), dest.Type())
|
|
}
|
|
}
|
|
|
|
func storeStruct(dest, src reflect.Value) error {
|
|
if isVariant(dest.Type()) {
|
|
return storeBase(dest, src)
|
|
}
|
|
dval := make([]interface{}, 0, dest.NumField())
|
|
dtype := dest.Type()
|
|
for i := 0; i < dest.NumField(); i++ {
|
|
field := dest.Field(i)
|
|
ftype := dtype.Field(i)
|
|
if ftype.PkgPath != "" {
|
|
continue
|
|
}
|
|
if ftype.Tag.Get("dbus") == "-" {
|
|
continue
|
|
}
|
|
dval = append(dval, field.Addr().Interface())
|
|
}
|
|
if src.Len() != len(dval) {
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"destination struct does not have "+
|
|
"enough fields need: %d have: %d",
|
|
src.Len(), len(dval))
|
|
}
|
|
return Store(src.Interface().([]interface{}), dval...)
|
|
}
|
|
|
|
func storeSliceIntoVariant(dest, src reflect.Value) error {
|
|
dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeSliceIntoInterface(dest, src reflect.Value) error {
|
|
var dv reflect.Value
|
|
if isVariant(src.Type().Elem()) {
|
|
//Convert variants to interface{} recursively when converting
|
|
//to interface{}
|
|
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
|
|
src.Len(), src.Cap())
|
|
} else {
|
|
dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
|
}
|
|
err := store(dv, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return storeBase(dest, dv)
|
|
}
|
|
|
|
func storeSliceIntoSlice(dest, src reflect.Value) error {
|
|
if dest.IsNil() || dest.Len() < src.Len() {
|
|
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
|
|
}
|
|
if dest.Len() != src.Len() {
|
|
return fmt.Errorf(
|
|
"dbus.Store: type mismatch: "+
|
|
"slices are different lengths "+
|
|
"need: %d have: %d",
|
|
src.Len(), dest.Len())
|
|
}
|
|
for i := 0; i < src.Len(); i++ {
|
|
err := store(dest.Index(i), getVariantValue(src.Index(i)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getVariantValue(in reflect.Value) reflect.Value {
|
|
if isVariant(in.Type()) {
|
|
return reflect.ValueOf(in.Interface().(Variant).Value())
|
|
}
|
|
return in
|
|
}
|
|
|
|
func isVariant(t reflect.Type) bool {
|
|
return t == variantType
|
|
}
|
|
|
|
// An ObjectPath is an object path as defined by the D-Bus spec.
|
|
type ObjectPath string
|
|
|
|
// IsValid returns whether the object path is valid.
|
|
func (o ObjectPath) IsValid() bool {
|
|
s := string(o)
|
|
if len(s) == 0 {
|
|
return false
|
|
}
|
|
if s[0] != '/' {
|
|
return false
|
|
}
|
|
if s[len(s)-1] == '/' && len(s) != 1 {
|
|
return false
|
|
}
|
|
// probably not used, but technically possible
|
|
if s == "/" {
|
|
return true
|
|
}
|
|
split := strings.Split(s[1:], "/")
|
|
for _, v := range split {
|
|
if len(v) == 0 {
|
|
return false
|
|
}
|
|
for _, c := range v {
|
|
if !isMemberChar(c) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
|
|
// documentation for more information about Unix file descriptor passsing.
|
|
type UnixFD int32
|
|
|
|
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
|
|
type UnixFDIndex uint32
|
|
|
|
// alignment returns the alignment of values of type t.
|
|
func alignment(t reflect.Type) int {
|
|
switch t {
|
|
case variantType:
|
|
return 1
|
|
case objectPathType:
|
|
return 4
|
|
case signatureType:
|
|
return 1
|
|
case interfacesType:
|
|
return 4
|
|
}
|
|
switch t.Kind() {
|
|
case reflect.Uint8:
|
|
return 1
|
|
case reflect.Uint16, reflect.Int16:
|
|
return 2
|
|
case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
|
return 4
|
|
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
|
return 8
|
|
case reflect.Ptr:
|
|
return alignment(t.Elem())
|
|
}
|
|
return 1
|
|
}
|
|
|
|
// isKeyType returns whether t is a valid type for a D-Bus dict.
|
|
func isKeyType(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
|
reflect.String, reflect.Uint, reflect.Int:
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isValidInterface returns whether s is a valid name for an interface.
|
|
func isValidInterface(s string) bool {
|
|
if len(s) == 0 || len(s) > 255 || s[0] == '.' {
|
|
return false
|
|
}
|
|
elem := strings.Split(s, ".")
|
|
if len(elem) < 2 {
|
|
return false
|
|
}
|
|
for _, v := range elem {
|
|
if len(v) == 0 {
|
|
return false
|
|
}
|
|
if v[0] >= '0' && v[0] <= '9' {
|
|
return false
|
|
}
|
|
for _, c := range v {
|
|
if !isMemberChar(c) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// isValidMember returns whether s is a valid name for a member.
|
|
func isValidMember(s string) bool {
|
|
if len(s) == 0 || len(s) > 255 {
|
|
return false
|
|
}
|
|
i := strings.Index(s, ".")
|
|
if i != -1 {
|
|
return false
|
|
}
|
|
if s[0] >= '0' && s[0] <= '9' {
|
|
return false
|
|
}
|
|
for _, c := range s {
|
|
if !isMemberChar(c) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func isMemberChar(c rune) bool {
|
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
|
(c >= 'a' && c <= 'z') || c == '_'
|
|
}
|