Repository navigation
Expand file tree
/
Copy pathasync_routine_manager.go
More file actions
145 lines (119 loc) · 3.91 KB
/
Copy pathasync_routine_manager.go
File metadata and controls
145 lines (119 loc) · 3.91 KB
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
145
package async
import (
"context"
"sync"
"time"
"github.com/google/uuid"
cmap "github.com/orcaman/concurrent-map/v2"
)
var routineManager AsyncRoutineManager
type AsyncRoutineManager interface {
AddObserver(observer RoutinesObserver) string
RemoveObserver(observerId string)
IsEnabled() bool
GetSnapshot() Snapshot
notify(eventSource func(observer RoutinesObserver))
Monitor() AsyncRoutineMonitor
register(routine AsyncRoutine)
deregister(routine AsyncRoutine)
}
type Toggle func() bool
type asyncRoutineManager struct {
ctx context.Context
managerToggle Toggle
snapshottingToggle Toggle
snapshottingInterval time.Duration
routines cmap.ConcurrentMap[string, AsyncRoutine]
observers cmap.ConcurrentMap[string, RoutinesObserver]
monitorLock sync.Mutex // user to sync the `Start` and `Stop` methods that are used to start the
// snapshotting routine
monitorStopChannel chan bool // Used to notify the snapshotting routine that it should stop running
monitorStarted bool
monitorWaitingGroup sync.WaitGroup // Used by the `Stop` method to wait for the snapshotting routine to end
}
func (arm *asyncRoutineManager) IsEnabled() bool {
return arm.managerToggle()
}
// AddObserver adds a new RoutineObserver to the list of observers.
// Assigns and returns an observer ID to the RoutineObserver
func (arm *asyncRoutineManager) AddObserver(observer RoutinesObserver) string {
uid := uuid.New().String()
arm.observers.Set(uid, observer)
return uid
}
// RemoveObserver removes the given RoutineObserver from the list of observers
func (arm *asyncRoutineManager) RemoveObserver(observerId string) {
arm.observers.Remove(observerId)
}
func (arm *asyncRoutineManager) GetSnapshot() Snapshot {
snapshot := newSnapshot()
for runningRoutines := range arm.routines.IterBuffered() {
snapshot.registerRoutine(runningRoutines.Val)
}
return snapshot
}
func (arm *asyncRoutineManager) notify(eventSource func(observer RoutinesObserver)) {
for _, observer := range arm.observers.Items() {
eventSource(observer)
}
}
func (arm *asyncRoutineManager) Monitor() AsyncRoutineMonitor {
return arm
}
func (arm *asyncRoutineManager) register(routine AsyncRoutine) {
arm.routines.Set(routine.id(), routine)
}
func (arm *asyncRoutineManager) deregister(routine AsyncRoutine) {
arm.routines.Remove(routine.id())
}
type AsyncManagerOption func(mgr *asyncRoutineManager)
func WithSnapshottingInterval(interval time.Duration) AsyncManagerOption {
return func(mgr *asyncRoutineManager) {
mgr.snapshottingInterval = interval
}
}
func WithSnapshottingToggle(toggle Toggle) AsyncManagerOption {
return func(mgr *asyncRoutineManager) {
mgr.snapshottingToggle = toggle
}
}
func WithManagerToggle(toggle Toggle) AsyncManagerOption {
return func(mgr *asyncRoutineManager) {
mgr.managerToggle = toggle
}
}
func WithContext(ctx context.Context) AsyncManagerOption {
return func(mgr *asyncRoutineManager) {
mgr.ctx = ctx
}
}
var lock sync.RWMutex
func newAsyncRoutineManager(options ...AsyncManagerOption) AsyncRoutineManager {
mgr := &asyncRoutineManager{
routines: cmap.New[AsyncRoutine](),
observers: cmap.New[RoutinesObserver](),
snapshottingInterval: DefaultRoutineSnapshottingInterval,
ctx: context.Background(),
managerToggle: func() bool { return true }, // manager is enabled by default
snapshottingToggle: func() bool { return true }, // snapshotting is enabled by default
monitorStopChannel: make(chan bool),
}
for _, option := range options {
option(mgr)
}
return mgr
}
func Manager(options ...AsyncManagerOption) AsyncRoutineManager {
if routineManager == nil {
lock.Lock()
defer lock.Unlock()
if routineManager == nil {
routineManager = newAsyncRoutineManager(options...)
return routineManager
}
}
for _, option := range options {
option(routineManager.(*asyncRoutineManager))
}
return routineManager
}