This repository was archived by the owner on Aug 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtask.go
More file actions
212 lines (184 loc) · 4 KB
/
Copy pathtask.go
File metadata and controls
212 lines (184 loc) · 4 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package gtasks
import (
"reflect"
"sync"
"time"
)
// New returns a new *runner to register tasks
func New() *Runner {
return &Runner{
tasks: make(map[string]*Task),
mu: sync.RWMutex{},
}
}
// Runner holds tasks that should be executed
type Runner struct {
tasks map[string]*Task
mu sync.RWMutex
}
// Run will start the tasks
func (r *Runner) Run() {
r.mu.RLock()
defer r.mu.RUnlock()
for _, task := range r.tasks {
go task.Run()
}
}
// Cancel stops a single task from executing: see Task.Cancel
func (r *Runner) Cancel(name string) {
r.mu.Lock()
if t, ok := r.tasks[name]; ok {
t.Cancel()
delete(r.tasks, name)
}
r.mu.Unlock()
}
// CancelAll stops all tasks from executing: see Task.Cancel
func (r *Runner) CancelAll() {
r.mu.Lock()
for name, t := range r.tasks {
t.Cancel()
delete(r.tasks, name)
}
r.mu.Unlock()
}
// Get returns a task by name
func (r *Runner) Get(name string) *Task {
r.mu.RLock()
defer r.mu.RUnlock()
if t, ok := r.tasks[name]; ok {
return t
}
return nil
}
// All returns all task
func (r *Runner) All() map[string]*Task {
r.mu.RLock()
defer r.mu.RUnlock()
return r.tasks
}
// Add adds a new task to a runner
func (r *Runner) Add(name string, f func(chan bool)) *Task {
t := NewTask(f)
r.mu.Lock()
r.tasks[name] = t
r.mu.Unlock()
return t
}
// NewTask creates a new taks an inits the needed variables
func NewTask(f func(chan bool)) *Task {
t := &Task{
f: f,
cancelchan: make(chan bool),
listeners: make([]chan interface{}, 0),
}
return t
}
// Task represents a task that can be run.
// It contains a at least runnable function
// that accepts a chan bool which will be closed
// if the task is canceled
type Task struct {
f func(chan bool)
cancelchan chan bool
after chan interface{}
listeners []chan interface{}
once bool
runStart time.Time
running bool
}
// Cancel stops a task. Executing functions
// should monitor if the cancelchan is closed
func (t *Task) Cancel() {
close(t.cancelchan)
}
// After sets the channel which will start the task.
// If it is not set the task will run immediately
// when calling Run and return after that
func (t *Task) After(c interface{}) *Task {
t.after = Wrap(c)
return t
}
// Now will send a message over the After channel
// to start the task
func (t *Task) Now() *Task {
if t.after != nil {
go func() {
t.after <- struct{}{}
}()
}
return t
}
// Once ensures a task is only ran once
func (t *Task) Once() *Task {
t.once = true
return t
}
// Subscribe returns a channel that can be used in the After func.
// This way tasks can be depedent on each other.
func (t *Task) Subscribe() chan interface{} {
c := make(chan interface{}, 1) // always make room for 1 item to be non-blocking
t.listeners = append(t.listeners, c)
return c
}
// Run will run a task
func (t *Task) Run() {
if t.after == nil {
t.exec()
return
}
for {
select {
case _, opened := <-t.cancelchan:
if opened == false {
return
}
case <-t.after:
t.exec()
if t.once {
return
}
}
}
}
func (t *Task) exec() {
t.running = true
t.runStart = time.Now()
t.f(t.cancelchan)
t.running = false
for _, l := range t.listeners {
select {
case l <- true:
default:
}
}
}
// Wrap is copied from https://github.com/eapache/channels
// Wrap takes any readable channel type (chan or <-chan but not chan<-) and
// exposes it as a SimpleOutChannel for easy integration with existing channel sources.
// It panics if the input is not a readable channel.
func Wrap(ch interface{}) chan interface{} {
t := reflect.TypeOf(ch)
if t.Kind() != reflect.Chan || t.ChanDir()&reflect.RecvDir == 0 {
panic("channels: input to Wrap must be readable channel")
}
realChan := make(chan interface{}) // buffer chan
if t.Elem().Kind() == reflect.Interface {
return ch.(chan interface{})
}
go func() {
v := reflect.ValueOf(ch)
for {
x, ok := v.Recv()
if !ok {
close(realChan)
return
}
select {
case realChan <- x.Interface():
default:
}
}
}()
return realChan
}