This repository was archived by the owner on Apr 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdaemon.go
More file actions
154 lines (124 loc) · 2.67 KB
/
Copy pathdaemon.go
File metadata and controls
154 lines (124 loc) · 2.67 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
package serverz
import (
"context"
"errors"
"net"
"sync"
"time"
)
// Daemon is a long-running process in the background.
type Daemon interface {
// Run has a loop inside and quits when it gets a signal from the quit channel.
Run(quit <-chan struct{}) error
}
// DaemonServer is a server without network, running a daemon.
type DaemonServer struct {
Daemon Daemon
mu sync.Mutex
doneChan chan struct{}
quitChan chan struct{}
}
func (s *DaemonServer) getQuitChan() <-chan struct{} {
s.mu.Lock()
defer s.mu.Unlock()
return s.getQuitChanLocked()
}
func (s *DaemonServer) getQuitChanLocked() chan struct{} {
if s.doneChan == nil {
s.doneChan = make(chan struct{})
}
return s.doneChan
}
func (s *DaemonServer) closeQuitChanLocked() {
ch := s.getDoneChanLocked()
select {
case <-ch:
// Already closed. Don't close again.
default:
// Safe to close here. We're the only closer,
// guarded by s.mu.
close(ch)
}
}
func (s *DaemonServer) getDoneChanLocked() chan struct{} {
if s.doneChan == nil {
s.doneChan = make(chan struct{})
}
return s.doneChan
}
func (s *DaemonServer) closeDoneChanLocked() {
ch := s.getDoneChanLocked()
select {
case <-ch:
// Already closed. Don't close again.
default:
// Safe to close here. We're the only closer,
// guarded by s.mu.
close(ch)
}
}
// Serve starts the daemon.
func (s *DaemonServer) Serve(l net.Listener) error {
if s.Daemon == nil {
return errors.New("no daemon specified")
}
err := s.Daemon.Run(s.getQuitChan())
s.mu.Lock()
s.closeDoneChanLocked()
s.mu.Unlock()
return err
}
// Shutdown gracefully stops the daemon by sending a quit signal to it and waiting for it to be done.
func (s *DaemonServer) Shutdown(ctx context.Context) error {
s.mu.Lock()
s.closeQuitChanLocked()
s.mu.Unlock()
select {
case <-s.doneChan:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
// Close sends a quit signal to the daemon.
func (s *DaemonServer) Close() error {
s.mu.Lock()
s.closeQuitChanLocked()
s.mu.Unlock()
return nil
}
// CronJob is ran scheduled by the CronDaemon.
type CronJob interface {
// Run runs the job.
Run() error
}
// CronDaemon is a daemon with an internal scheduler for a CronJob.
type CronDaemon struct {
Job CronJob
Ticker *time.Ticker
}
// Run implements the Daemon interface.
func (d *CronDaemon) Run(quit <-chan struct{}) error {
if d.Job == nil {
return errors.New("no job specified")
}
if d.Ticker == nil {
return d.Job.Run()
}
errChan := make(chan error)
for {
select {
case <-quit:
return nil
case err := <-errChan:
return err
case <-d.Ticker.C:
go func() {
err := d.Job.Run()
if err != nil {
errChan <- err
}
}()
}
}
}