-
Notifications
You must be signed in to change notification settings - Fork 61
/
instance_manager.go
144 lines (117 loc) · 3.77 KB
/
instance_manager.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
143
144
package instancemgmt
import (
"reflect"
"sync"
"github.com/grafana/grafana-plugin-sdk-go/backend"
)
// Instance is a marker interface for an instance.
type Instance interface{}
// InstanceDisposer is implemented by an Instance that has a Dispose method,
// which defines that the instance is disposable.
//
// InstanceManager will call the Dispose method before an Instance is replaced
// with a new Instance. This allows an Instance to clean up resources in use,
// if any.
type InstanceDisposer interface {
Dispose()
}
// InstanceCallbackFunc defines the callback function of the InstanceManager.Do method.
// The argument provided will of type Instance.
type InstanceCallbackFunc interface{}
// InstanceManager manages the lifecycle of instances.
type InstanceManager interface {
// Get returns an Instance.
//
// If Instance is cached and not updated it's returned. If Instance is not cached or
// updated, a new Instance is created and cached before returned.
Get(pluginContext backend.PluginContext) (Instance, error)
// Do provides an Instance as argument to fn.
//
// If Instance is cached and not updated provides as argument to fn. If Instance is not cached or
// updated, a new Instance is created and cached before provided as argument to fn.
Do(pluginContext backend.PluginContext, fn InstanceCallbackFunc) error
}
// CachedInstance a cached Instance.
type CachedInstance struct {
PluginContext backend.PluginContext
instance Instance
}
// InstanceProvider defines an instance provider, providing instances.
type InstanceProvider interface {
// GetKey returns a cache key to be used for caching an Instance.
GetKey(pluginContext backend.PluginContext) (interface{}, error)
// NeedsUpdate returns whether a cached Instance have been updated.
NeedsUpdate(pluginContext backend.PluginContext, cachedInstance CachedInstance) bool
// NewInstance creates a new Instance.
NewInstance(pluginContext backend.PluginContext) (Instance, error)
}
// New create a new instance manager.
func New(provider InstanceProvider) InstanceManager {
if provider == nil {
panic("provider cannot be nil")
}
return &instanceManager{
provider: provider,
cache: sync.Map{},
locker: newLocker(),
}
}
type instanceManager struct {
locker *locker
provider InstanceProvider
cache sync.Map
}
func (im *instanceManager) Get(pluginContext backend.PluginContext) (Instance, error) {
cacheKey, err := im.provider.GetKey(pluginContext)
if err != nil {
return nil, err
}
// Double-checked locking for update/create criteria
im.locker.RLock(cacheKey)
item, ok := im.cache.Load(cacheKey)
im.locker.RUnlock(cacheKey)
if ok {
ci := item.(CachedInstance)
needsUpdate := im.provider.NeedsUpdate(pluginContext, ci)
if !needsUpdate {
return ci.instance, nil
}
}
im.locker.Lock(cacheKey)
defer im.locker.Unlock(cacheKey)
if item, ok := im.cache.Load(cacheKey); ok {
ci := item.(CachedInstance)
needsUpdate := im.provider.NeedsUpdate(pluginContext, ci)
if !needsUpdate {
return ci.instance, nil
}
if disposer, valid := ci.instance.(InstanceDisposer); valid {
disposer.Dispose()
}
}
instance, err := im.provider.NewInstance(pluginContext)
if err != nil {
return nil, err
}
im.cache.Store(cacheKey, CachedInstance{
PluginContext: pluginContext,
instance: instance,
})
return instance, nil
}
func (im *instanceManager) Do(pluginContext backend.PluginContext, fn InstanceCallbackFunc) error {
if fn == nil {
panic("fn cannot be nil")
}
instance, err := im.Get(pluginContext)
if err != nil {
return err
}
callInstanceHandlerFunc(fn, instance)
return nil
}
func callInstanceHandlerFunc(fn InstanceCallbackFunc, instance interface{}) {
var params = []reflect.Value{}
params = append(params, reflect.ValueOf(instance))
reflect.ValueOf(fn).Call(params)
}