-
Notifications
You must be signed in to change notification settings - Fork 0
/
strings.go
142 lines (124 loc) · 3.99 KB
/
strings.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2013-2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package watcher
import (
"github.com/juju/errors"
"github.com/juju/juju/worker/catacomb"
)
// StringsChannel is a change channel as described in the CoreWatcher docs.
//
// It sends a single value indicating a baseline set of values, and subsequent
// values representing additions, changes, and/or removals of those values. The
// precise semantics may depend upon the individual watcher.
type StringsChannel <-chan []string
// StringsWatcher conveniently ties a StringsChannel to the worker.Worker that
// represents its validity.
type StringsWatcher interface {
CoreWatcher
Changes() StringsChannel
}
// StringsHandler defines the operation of a StringsWorker.
type StringsHandler interface {
// SetUp is called once when creating a StringsWorker. It must return a
// StringsWatcher or an error. The StringsHandler takes responsibility for
// stopping any returned watcher and handling any errors.
SetUp() (StringsWatcher, error)
// Handle is called with every value received from the StringsWatcher
// returned by SetUp. If it returns an error, the StringsWorker will be
// stopped.
//
// If Handle runs any blocking operations it must pass through, or select
// on, the supplied abort channel; this channel will be closed when the
// StringsWorker is killed. An aborted Handle should not return an error.
Handle(abort <-chan struct{}, changes []string) error
// TearDown is called once when stopping a StringsWorker, whether or not
// SetUp succeeded. It need not concern itself with the StringsWatcher, but
// must clean up any other resources created in SetUp or Handle.
TearDown() error
}
// StringsConfig holds the direct dependencies of a StringsWorker.
type StringsConfig struct {
Handler StringsHandler
}
// Validate returns ann error if the config cannot start a StringsWorker.
func (config StringsConfig) Validate() error {
if config.Handler == nil {
return errors.NotValidf("nil Handler")
}
return nil
}
// NewStringsWorker starts a new worker that runs a StringsHandler.
func NewStringsWorker(config StringsConfig) (*StringsWorker, error) {
if err := config.Validate(); err != nil {
return nil, errors.Trace(err)
}
sw := &StringsWorker{
config: config,
}
err := catacomb.Invoke(catacomb.Plan{
Site: &sw.catacomb,
Work: sw.loop,
})
if err != nil {
return nil, errors.Trace(err)
}
return sw, nil
}
// StringsWorker is a worker that wraps a StringsWatcher.
type StringsWorker struct {
config StringsConfig
catacomb catacomb.Catacomb
}
func (sw *StringsWorker) loop() (err error) {
changes := sw.setUp()
defer sw.tearDown(err)
abort := sw.catacomb.Dying()
for {
select {
case <-abort:
return sw.catacomb.ErrDying()
case strings, ok := <-changes:
if !ok {
return errors.New("change channel closed")
}
err = sw.config.Handler.Handle(abort, strings)
if err != nil {
return err
}
}
}
}
// setUp calls the handler's SetUp method; registers any returned watcher with
// the worker's catacomb; and returns the watcher's changes channel. Any errors
// encountered kill the worker and cause a nil channel to be returned.
func (sw *StringsWorker) setUp() StringsChannel {
watcher, err := sw.config.Handler.SetUp()
if err != nil {
sw.catacomb.Kill(err)
}
if watcher == nil {
sw.catacomb.Kill(errors.New("handler returned nil watcher"))
} else {
if err := sw.catacomb.Add(watcher); err != nil {
sw.catacomb.Kill(err)
} else {
return watcher.Changes()
}
}
return nil
}
// tearDown kills the worker with the supplied error; and then kills it with
// any error returned by the handler's TearDown method.
func (sw *StringsWorker) tearDown(err error) {
sw.catacomb.Kill(err)
err = sw.config.Handler.TearDown()
sw.catacomb.Kill(err)
}
// Kill is part of the worker.Worker interface.
func (sw *StringsWorker) Kill() {
sw.catacomb.Kill(nil)
}
// Wait is part of the worker.Worker interface.
func (sw *StringsWorker) Wait() error {
return sw.catacomb.Wait()
}