Skip to content

Commit

Permalink
Merge pull request #310 from godbus/fix/prop_pointers
Browse files Browse the repository at this point in the history
prop: copy initial values and store as pointers internally
  • Loading branch information
guelfey committed Feb 13, 2022
2 parents 3775a5b + e230c8c commit b357b44
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 15 deletions.
46 changes: 31 additions & 15 deletions prop/prop.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ const IntrospectDataString = `
// Prop represents a single property. It is used for creating a Properties
// value.
type Prop struct {
// Initial value. Must be a DBus-representable type.
// Initial value. Must be a DBus-representable type. This is not modified
// after Properties has been initialized; use Get or GetMust to access the
// value.
Value interface{}

// If true, the value can be modified by calls to Set.
Expand Down Expand Up @@ -154,7 +156,7 @@ func (p *Prop) Introspection(name string) introspect.Property {
}
result.Annotations = []introspect.Annotation{
{
Name: "org.freedesktop.DBus.Property.EmitsChangedSignal",
Name: "org.freedesktop.DBus.Property.EmitsChangedSignal",
Value: p.Emit.String(),
},
}
Expand All @@ -173,7 +175,7 @@ type Change struct {
// using the org.freedesktop.DBus.Properties interface. It is safe for
// concurrent use by multiple goroutines.
type Properties struct {
m map[string]map[string]*Prop
m Map
mut sync.RWMutex
conn *dbus.Conn
path dbus.ObjectPath
Expand All @@ -183,7 +185,7 @@ type Properties struct {
// swallowing the error, shouldn't be used.
//
// Deprecated: use Export instead.
func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop) *Properties {
func New(conn *dbus.Conn, path dbus.ObjectPath, props Map) *Properties {
p, err := Export(conn, path, props)
if err != nil {
return nil
Expand All @@ -196,15 +198,33 @@ func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Pro
// second-level key is the name of the property. The returned structure will be
// exported as org.freedesktop.DBus.Properties on path.
func Export(
conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop,
conn *dbus.Conn, path dbus.ObjectPath, props Map,
) (*Properties, error) {
p := &Properties{m: props, conn: conn, path: path}
p := &Properties{m: copyProps(props), conn: conn, path: path}
if err := conn.Export(p, path, "org.freedesktop.DBus.Properties"); err != nil {
return nil, err
}
return p, nil
}

// Map is a helper type for supplying the configuration of properties to be handled.
type Map = map[string]map[string]*Prop

func copyProps(in Map) Map {
out := make(Map, len(in))
for intf, props := range in {
out[intf] = make(map[string]*Prop)
for name, prop := range props {
out[intf][name] = new(Prop)
*out[intf][name] = *prop
val := reflect.New(reflect.TypeOf(prop.Value))
val.Elem().Set(reflect.ValueOf(prop.Value))
out[intf][name].Value = val.Interface()
}
}
return out
}

// Get implements org.freedesktop.DBus.Properties.Get.
func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
p.mut.RLock()
Expand All @@ -217,7 +237,7 @@ func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
if !ok {
return dbus.Variant{}, ErrPropNotFound
}
return dbus.MakeVariant(prop.Value), nil
return dbus.MakeVariant(reflect.ValueOf(prop.Value).Elem().Interface()), nil
}

// GetAll implements org.freedesktop.DBus.Properties.GetAll.
Expand All @@ -230,7 +250,7 @@ func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error)
}
rm := make(map[string]dbus.Variant, len(m))
for k, v := range m {
rm[k] = dbus.MakeVariant(v.Value)
rm[k] = dbus.MakeVariant(reflect.ValueOf(v.Value).Elem().Interface())
}
return rm, nil
}
Expand All @@ -240,7 +260,7 @@ func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error)
func (p *Properties) GetMust(iface, property string) interface{} {
p.mut.RLock()
defer p.mut.RUnlock()
return p.m[iface][property].Value
return reflect.ValueOf(p.m[iface][property].Value).Elem().Interface()
}

// Introspection returns the introspection data that represents the properties
Expand All @@ -260,9 +280,6 @@ func (p *Properties) Introspection(iface string) []introspect.Property {
// must already be locked.
func (p *Properties) set(iface, property string, v interface{}) error {
prop := p.m[iface][property]
if reflect.ValueOf(prop.Value).Kind() != reflect.Ptr {
prop.Value = reflect.New(reflect.TypeOf(prop.Value)).Interface()
}
err := dbus.Store([]interface{}{v}, prop.Value)
if err != nil {
return err
Expand Down Expand Up @@ -324,9 +341,8 @@ func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error
func (p *Properties) SetMust(iface, property string, v interface{}) {
p.mut.Lock()
defer p.mut.Unlock() // unlock in case of panic
prop := p.m[iface][property]
prop.Value = v
if err := p.emitChange(iface, property); err != nil {
err := p.set(iface, property, v)
if err != nil {
panic(err)
}
}
44 changes: 44 additions & 0 deletions prop/prop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,47 @@ func TestValidateStructsAsProp(t *testing.T) {
comparePropValue(obj, "FooStructPtr", *zooPtr, t)
comparePropValue(obj, "SliceOfFoos", zoos, t)
}

func TestInt32(t *testing.T) {
srv, err := dbus.SessionBus()
if err != nil {
t.Fatal(err)
}
defer srv.Close()

cli, err := dbus.SessionBus()
if err != nil {
t.Fatal(err)
}
defer cli.Close()

propsSpec := map[string]map[string]*Prop{
"org.guelfey.DBus.Test": {
"int32": {
int32(100),
true,
EmitTrue,
nil,
},
},
}
props := New(srv, "/org/guelfey/DBus/Test", propsSpec)

obj := cli.Object(srv.Names()[0], "/org/guelfey/DBus/Test")

comparePropValue(obj, "int32", int32(100), t)
r := props.GetMust("org.guelfey.DBus.Test", "int32")
if r != int32(100) {
t.Errorf("expected r to be int32(100), but was %#v", r)
}

if err := props.Set("org.guelfey.DBus.Test", "int32", dbus.MakeVariant(int32(101))); err != nil {
t.Fatalf("failed to set prop int32 to 101")
}

comparePropValue(obj, "int32", int32(101), t)
r = props.GetMust("org.guelfey.DBus.Test", "int32")
if r != int32(101) {
t.Errorf("expected r to be int32(101), but was %#v", r)
}
}

0 comments on commit b357b44

Please sign in to comment.