Merge lp:~kyrofa/go-ual/observers_collection into lp:go-ual

Proposed by Kyle Fazzari
Status: Merged
Approved by: dobey
Approved revision: 7
Merged at revision: 3
Proposed branch: lp:~kyrofa/go-ual/observers_collection
Merge into: lp:go-ual
Prerequisite: lp:~kyrofa/go-ual/general_helpers
Diff against target: 198 lines (+189/-0)
2 files modified
ual/observers_collection.go (+114/-0)
ual/observers_collection_test.go (+75/-0)
To merge this branch: bzr merge lp:~kyrofa/go-ual/observers_collection
Reviewer Review Type Date Requested Status
dobey (community) Approve
Review via email: mp+271403@code.launchpad.net

Commit message

Add an Go-side observer tracker.

Description of the change

Add an Go-side observer tracker.

This is necessary due to Go's inability to pack functions into C function pointers, so we'll have to make gateway functions for them and track the Go side ourselves.

To post a comment you must log in.
2. By Kyle Fazzari

Implement bindings for various UAL helper methods.

Specifically, GetPrimaryPid(), StartSessionHelper()
and StopMultipleHelper().

Signed-off-by: Kyle Fazzari <email address hidden>

3. By Kyle Fazzari

Restrict the license header to version 3 of the GPL.

Signed-off-by: Kyle Fazzari <email address hidden>

4. By Kyle Fazzari

Change license to LGPLv3.

Signed-off-by: Kyle Fazzari <email address hidden>

5. By Kyle Fazzari

Add an Go-side observer tracker.

This is necessary due to Go's inability to pack functions into
C function pointers, so we'll have to make gateway functions
for them and track the Go side ourselves.

Signed-off-by: Kyle Fazzari <email address hidden>

6. By Kyle Fazzari

Restrict the license headers to version 3 of the GPL.

Signed-off-by: Kyle Fazzari <email address hidden>

7. By Kyle Fazzari

Change license to LGPLv3.

Signed-off-by: Kyle Fazzari <email address hidden>

Revision history for this message
dobey (dobey) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'ual/observers_collection.go'
2--- ual/observers_collection.go 1970-01-01 00:00:00 +0000
3+++ ual/observers_collection.go 2015-09-17 18:06:40 +0000
4@@ -0,0 +1,114 @@
5+/* Copyright (C) 2015 Canonical Ltd.
6+ *
7+ * This file is part of go-ual.
8+ *
9+ * go-ual is free software: you can redistribute it and/or modify it under the
10+ * terms of the GNU Lesser General Public License version 3, as published by the
11+ * Free Software Foundation.
12+ *
13+ * go-ual is distributed in the hope that it will be useful, but WITHOUT ANY
14+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16+ * details.
17+ *
18+ * You should have received a copy of the GNU Lesser General Public License
19+ * along with go-ual. If not, see <http://www.gnu.org/licenses/>.
20+ */
21+
22+package ual
23+
24+import (
25+ "fmt"
26+ "sync"
27+ "unsafe"
28+)
29+
30+type Observer func(string, string, string)
31+type ObserverId int
32+
33+// ObserversCollection is responsible for tracking observers for a given event.
34+// It exists for two reasons. First of all, we can't pass Go functions into C.
35+// Second, if we packed the function into the UAL `user_data` Go would lose
36+// track of it and it'd be garbage-collected. ObserversCollection can safely be
37+// accessed simultaneously from multiple threads.
38+type ObserversCollection struct {
39+ mutex sync.RWMutex
40+ observers map[ObserverId]observerAndHelperType
41+ observerId ObserverId
42+}
43+
44+// observerAndHelperType associates an observer and a helper type.
45+type observerAndHelperType struct {
46+ observerPointer unsafe.Pointer
47+ helperType string
48+}
49+
50+// AddObserver adds an observer to the collection and returns a unique ID for
51+// it. This ID is used for all further ObserversCollection communication for
52+// that observer (e.g. calling the observer, removing it, etc.). It also
53+// returns an unsafe.Pointer for the observer which can be passed into C.
54+func (collection *ObserversCollection) AddObserver(helperType string,
55+ observer Observer) ObserverId {
56+ collection.mutex.Lock()
57+ defer collection.mutex.Unlock()
58+
59+ if collection.observers == nil {
60+ collection.observers = make(map[ObserverId]observerAndHelperType)
61+ }
62+
63+ collection.observerId += 1
64+
65+ collection.observers[collection.observerId] = observerAndHelperType{
66+ observerPointer: unsafe.Pointer(&observer),
67+ helperType: helperType,
68+ }
69+
70+ return collection.observerId
71+}
72+
73+// RemoveObserver removes the observer represented by the given ID from the
74+// collection.
75+func (collection *ObserversCollection) RemoveObserver(id ObserverId) bool {
76+ collection.mutex.Lock()
77+ defer collection.mutex.Unlock()
78+
79+ // Reading from a potentially nil map is okay-- it will behave as if it was
80+ // empty.
81+ _, ok := collection.observers[id]
82+ if ok {
83+ delete(collection.observers, id)
84+ return true
85+ }
86+
87+ return false
88+}
89+
90+// Observer returns the unsafe.Pointer for a given observer.
91+func (collection *ObserversCollection) Observer(id ObserverId) (unsafe.Pointer, error) {
92+ collection.mutex.RLock()
93+ defer collection.mutex.RUnlock()
94+
95+ // Reading from a potentially nil map is okay-- it will behave as if it was
96+ // empty.
97+ observerAndHelper, ok := collection.observers[id]
98+ if ok {
99+ return observerAndHelper.observerPointer, nil
100+ }
101+
102+ return nil, fmt.Errorf("No observer with ID %d", id)
103+}
104+
105+// ObserverHelperType returns the helper type for a given observer.
106+func (collection *ObserversCollection) ObserverHelperType(id ObserverId) (string, error) {
107+ collection.mutex.RLock()
108+ defer collection.mutex.RUnlock()
109+
110+ // Reading from a potentially nil map is okay-- it will behave as if it was
111+ // empty.
112+ observerAndHelper, ok := collection.observers[id]
113+ if ok {
114+ return observerAndHelper.helperType, nil
115+ }
116+
117+ return "", fmt.Errorf("No observer with ID %d", id)
118+}
119
120=== added file 'ual/observers_collection_test.go'
121--- ual/observers_collection_test.go 1970-01-01 00:00:00 +0000
122+++ ual/observers_collection_test.go 2015-09-17 18:06:40 +0000
123@@ -0,0 +1,75 @@
124+/* Copyright (C) 2015 Canonical Ltd.
125+ *
126+ * This file is part of go-ual.
127+ *
128+ * go-ual is free software: you can redistribute it and/or modify it under the
129+ * terms of the GNU Lesser General Public License version 3, as published by the
130+ * Free Software Foundation.
131+ *
132+ * go-ual is distributed in the hope that it will be useful, but WITHOUT ANY
133+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
134+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
135+ * details.
136+ *
137+ * You should have received a copy of the GNU Lesser General Public License
138+ * along with go-ual. If not, see <http://www.gnu.org/licenses/>.
139+ */
140+
141+package ual
142+
143+import (
144+ "testing"
145+)
146+
147+// Test typical observer usage: add, call and delete
148+func TestObserversCollection(t *testing.T) {
149+ collection := ObserversCollection{}
150+
151+ if len(collection.observers) > 0 {
152+ t.Errorf("Observers collection has %d observers, expected it to be empty",
153+ len(collection.observers))
154+ }
155+
156+ called := false
157+ id := collection.AddObserver("foo", func(a, b, c string) {
158+ called = true
159+ })
160+
161+ if len(collection.observers) != 1 {
162+ t.Errorf("Observers collection has %d observers, expected 1",
163+ len(collection.observers))
164+ }
165+
166+ helperType, err := collection.ObserverHelperType(id)
167+ if helperType != "foo" {
168+ t.Errorf(`Helper type was "%s", expected "foo"`, helperType)
169+ }
170+
171+ observerPointer, err := collection.Observer(id)
172+ if err != nil {
173+ t.Errorf("Unexpected error obtaining observer: %s", err)
174+ }
175+
176+ (*(*Observer)(observerPointer))("foo", "bar", "baz")
177+
178+ if !called {
179+ t.Errorf("Expected observer to be called")
180+ }
181+
182+ if !collection.RemoveObserver(id) {
183+ t.Error("Unexpected error removing observer")
184+ }
185+
186+ if len(collection.observers) > 0 {
187+ t.Errorf("Observers collection has %d observers, expected it to be empty",
188+ len(collection.observers))
189+ }
190+
191+ called = false
192+ // Should still be able to call the observer
193+ (*(*Observer)(observerPointer))("foo", "bar", "baz")
194+
195+ if !called {
196+ t.Errorf("Expected observer to be called")
197+ }
198+}

Subscribers

People subscribed via source and target branches

to all changes: