/
delay.go
133 lines (118 loc) · 2.67 KB
/
delay.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
package trigger
import (
"bytes"
"encoding/json"
"fmt"
"time"
"github.com/proidiot/gone/log"
"github.com/stuphlabs/pullcord/config"
)
// DelayTrigger is a Triggerrer that delays the execution of another
// trigger for at least a minimum amount of time after the most recent request.
// The obvious analogy would be a screen saver, which will start after a
// certain period has elapsed, but the timer is reset quite often.
type DelayTrigger struct {
DelayedTrigger Triggerrer
Delay time.Duration
c chan<- interface{}
}
func init() {
config.MustRegisterResourceType(
"delaytrigger",
func() json.Unmarshaler {
return new(DelayTrigger)
},
)
}
// UnmarshalJSON implements encoding/json.Unmarshaler.
func (d *DelayTrigger) UnmarshalJSON(input []byte) error {
var t struct {
DelayedTrigger config.Resource
Delay string
}
dec := json.NewDecoder(bytes.NewReader(input))
if e := dec.Decode(&t); e != nil {
return e
}
dt := t.DelayedTrigger.Unmarshalled
switch dt := dt.(type) {
case Triggerrer:
d.DelayedTrigger = dt
default:
_ = log.Err(
fmt.Sprintf(
"Registry value is not a Trigger: %s",
dt,
),
)
return config.UnexpectedResourceType
}
dp, e := time.ParseDuration(t.Delay)
if e != nil {
return e
}
d.Delay = dp
return nil
}
// NewDelayTrigger initializes a DelayTrigger. It might not be strictly
// necessary anymore.
func NewDelayTrigger(
delayedTrigger Triggerrer,
delay time.Duration,
) *DelayTrigger {
return &DelayTrigger{
delayedTrigger,
delay,
nil,
}
}
func delaytrigger(
tr Triggerrer,
dla time.Duration,
ac <-chan interface{},
) {
tmr := time.NewTimer(dla)
for {
select {
case _, ok := <-ac:
if tmr != nil && !tmr.Stop() {
<-tmr.C
}
if !ok {
return
}
tmr.Reset(dla)
_ = log.Debug("delaytrigger has been reset")
case <-tmr.C:
_ = log.Debug("delaytrigger has expired")
if err := tr.Trigger(); err != nil {
_ = log.Err(
fmt.Sprintf(
"delaytrigger received an"+
" error: %#v",
err,
),
)
}
tmr.Stop()
}
}
}
// Trigger sets or resets the delay after which it will execute the child
// trigger. The child trigger will be executed no sooner than the delay time
// after any particular call, but subsequent calls may extend that time out
// further (possibly indefinitely).
func (d *DelayTrigger) Trigger() error {
_ = log.Debug("delaytrigger initiated")
if d.c == nil {
_ = log.Debug("creating delay timer")
fc := make(chan interface{})
d.c = fc
go delaytrigger(d.DelayedTrigger, d.Delay, fc)
} else {
_ = log.Debug("resetting delay timer")
d.c <- nil
}
_ = log.Debug("delaytrigger completed")
return nil
}