Skip to content

Commit f105bb0

Browse files
cleanup and callbacks
1 parent 21c53da commit f105bb0

File tree

5 files changed

+271
-62
lines changed

5 files changed

+271
-62
lines changed

README.md

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,28 @@ import (
1919

2020
func main() {
2121

22-
// the update function
23-
f := func(x interface{}) interface{} {
24-
return fmt.Sprintf("%s-", x)
22+
// the function to call
23+
var update = func(x interface{}) (interface{}, error) {
24+
y := x
25+
if y == nil { y = "" }
26+
return fmt.Sprintf("%s-", y), nil
2527
}
2628

27-
// creating the new resource
28-
r := updatingresource.NewUpdatingResource("-", f, 500 * time.Millisecond)
29-
30-
// query the resource 8 times
31-
for i := 0; i <8; i++{
32-
time.Sleep(200 * time.Millisecond)
33-
x := r.Get().(string)
34-
fmt.Printf("%s\n", x)
35-
36-
// stop updating after the 6th time
37-
if i == 6 {
38-
r.Done()
39-
}
29+
// the resource config
30+
config := updatingresource.Config{
31+
Name: "dashes",
32+
Update: update,
33+
Interval: 20 * time.Millisecond,
4034
}
35+
resource := config.NewResource()
36+
37+
fmt.Printf("%s\n", resource.Get().(string))
38+
39+
time.Sleep(30 * time.Millisecond)
40+
fmt.Printf("%s\n", resource.Get().(string))
41+
42+
time.Sleep(20 * time.Millisecond)
43+
fmt.Printf("%s\n", resource.Get().(string))
44+
4145
}
4246
~~~~

example_basic_test.go

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,34 @@ import (
99

1010
func Example() {
1111

12-
// the update function
13-
f := func(x interface{}) interface{} {
14-
return fmt.Sprintf("%s-", x)
12+
// the function to call
13+
var update = func(x interface{}) (interface{}, error) {
14+
y := x
15+
if y == nil { y = "" }
16+
return fmt.Sprintf("%s-", y), nil
1517
}
1618

17-
// creating the new resource
18-
r := updatingresource.NewUpdatingResource("-", f, 500 * time.Millisecond)
19+
// the resource config
20+
config := updatingresource.Config{
21+
Name: "dashes",
22+
Update: update,
23+
Interval: 300 * time.Millisecond,
24+
}
25+
resource := config.NewResource()
1926

20-
// query the resource 8 times
21-
for i := 0; i <8; i++{
22-
time.Sleep(200 * time.Millisecond)
23-
x := r.Get().(string)
24-
fmt.Printf("%s\n", x)
27+
fmt.Printf("%s\n", resource.Get().(string))
2528

26-
// stop updating after the 6th time
27-
if i == 6 {
28-
r.Done()
29-
}
30-
}
29+
resource.Tick()
30+
time.Sleep(50 * time.Millisecond)
31+
32+
fmt.Printf("%s\n", resource.Get().(string))
33+
34+
time.Sleep(450 * time.Millisecond)
35+
36+
fmt.Printf("%s\n", resource.Get().(string))
3137

3238
// Output:
3339
// -
34-
// -
35-
// --
3640
// --
3741
// ---
38-
// ---
39-
// ---
40-
// ---
4142
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module github.com/sebogh/updating-resource
22

33
go 1.15
4+

updatingresource.go

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,122 @@ import (
55
"time"
66
)
77

8-
// UpdatingResource is a structure to wrap an object x which is regularly and
9-
// asynchronously computed / updated.
10-
type UpdatingResource struct {
8+
// Config is the configuration for a Resource.
9+
type Config struct {
10+
11+
// Name of this resource.
12+
Name string
13+
14+
// Update is the function to update the wrapped object. Update must handle nil
15+
// values.
16+
Update func(x interface{}) (interface{}, error)
17+
18+
// Interval is the time to wait between between automatic updates. If 0 (or not
19+
// set), updates will never trigger automatically.
20+
Interval time.Duration
21+
22+
// Success is the function (if any) to call, if the object was successfully
23+
// updated. Success is called with the new value the name of the resource.
24+
Success func(x interface{}, name string)
25+
26+
// Success is the function (if any) to call, if the object was successfully
27+
// updated. Error is called with the the error that occurred the name of the
28+
// resource.
29+
Error func(e error, name string)
30+
}
31+
32+
// Resource regularly, asynchronously updates a wrapped object.
33+
type Resource struct {
34+
35+
// resource configuration
36+
*Config
37+
38+
// mutex for protecting object updates
1139
mu *sync.RWMutex
12-
x *interface{}
40+
41+
// the wrapped object
42+
x *interface{}
43+
44+
// a channel for terminating the updates
1345
done chan bool
46+
47+
// channel to manually trigger an update
48+
tick chan bool
49+
1450
}
1551

16-
// NewUpdatingResource creates a new UpdatingResource. Thereby, f is the function
17-
// that will be called every ttl to compute a new value for x (i.e. x=f(x)).
18-
func NewUpdatingResource(x interface{}, f func(x interface{}) interface{}, ttl time.Duration) *UpdatingResource {
52+
// NewResource creates a new Resource.
53+
func (c *Config) NewResource() *Resource {
1954
var mu sync.RWMutex
55+
2056
done := make(chan bool)
21-
go func(f func(x interface{}) interface{}) {
22-
ticker := time.NewTicker(ttl)
57+
tick := make(chan bool)
58+
59+
var startValue interface{} = nil
60+
x := &startValue
61+
62+
var timeTicker <-chan time.Time
63+
if c.Interval > 0 {
64+
timeTicker = time.NewTicker(c.Interval).C
65+
}
66+
67+
go func(f func(x interface{}) (interface{}, error)) {
68+
69+
// wrap f to be used for inputs on multiple channels
70+
var fWrapper = func() {
71+
y, err := f(*x)
72+
if err != nil && c.Error != nil {
73+
c.Error(err, c.Name)
74+
} else {
75+
if c.Success != nil {
76+
c.Success(y, c.Name)
77+
}
78+
mu.Lock()
79+
*x = y
80+
mu.Unlock()
81+
}
82+
}
83+
84+
// wait (forever) on channels
2385
for {
2486
select {
2587
case <-done:
2688
return
27-
case <-ticker.C:
28-
y := f(x)
29-
mu.Lock()
30-
x = y
31-
mu.Unlock()
89+
case <-timeTicker:
90+
fWrapper()
91+
case <-tick:
92+
fWrapper()
3293
}
3394
}
34-
}(f)
35-
resource := UpdatingResource{x: &x, mu: &mu, done: done}
95+
}(c.Update)
96+
97+
// compute step 0
98+
tick <- true
99+
100+
resource := Resource{
101+
Config: c,
102+
mu: &mu,
103+
x: x,
104+
done: done,
105+
tick: tick,
106+
}
36107
return &resource
37108
}
38109

39110
// Get returns the current value of the wrapped object. Get is thread-safe
40111
// wrt. to the function updating the encapsulated object.
41-
func (r *UpdatingResource) Get() interface{} {
112+
func (r *Resource) Get() interface{} {
42113
r.mu.RLock()
43114
defer r.mu.RUnlock()
44115
return *r.x
45116
}
46117

47118
// Done stops the updating of the wrapped object.
48-
func (r *UpdatingResource) Done() {
119+
func (r *Resource) Done() {
49120
r.done <- true
50-
}
121+
}
122+
123+
// Tick manually trigger an update.
124+
func (r *Resource) Tick() {
125+
r.tick <- true
126+
}

0 commit comments

Comments
 (0)