@@ -2,6 +2,7 @@ package simple
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"fmt"
6
7
"time"
7
8
@@ -18,14 +19,21 @@ import (
18
19
19
20
var logR = logging .Logger ("reprovider.simple" )
20
21
22
+ // ErrClosed is returned by Trigger when operating on a closed reprovider.
23
+ var ErrClosed = errors .New ("reprovider service stopped" )
24
+
21
25
// KeyChanFunc is function streaming CIDs to pass to content routing
22
26
type KeyChanFunc func (context.Context ) (<- chan cid.Cid , error )
23
- type doneFunc func (error )
24
27
25
28
// Reprovider reannounces blocks to the network
26
29
type Reprovider struct {
27
- ctx context.Context
28
- trigger chan doneFunc
30
+ // Reprovider context. Cancel to stop, then wait on closedCh.
31
+ ctx context.Context
32
+ cancel context.CancelFunc
33
+ closedCh chan struct {}
34
+
35
+ // Trigger triggers a reprovide.
36
+ trigger chan chan <- error
29
37
30
38
// The routing system to provide values through
31
39
rsys routing.ContentRouting
@@ -37,9 +45,12 @@ type Reprovider struct {
37
45
38
46
// NewReprovider creates new Reprovider instance.
39
47
func NewReprovider (ctx context.Context , reprovideIniterval time.Duration , rsys routing.ContentRouting , keyProvider KeyChanFunc ) * Reprovider {
48
+ ctx , cancel := context .WithCancel (ctx )
40
49
return & Reprovider {
41
- ctx : ctx ,
42
- trigger : make (chan doneFunc ),
50
+ ctx : ctx ,
51
+ cancel : cancel ,
52
+ closedCh : make (chan struct {}),
53
+ trigger : make (chan chan <- error ),
43
54
44
55
rsys : rsys ,
45
56
keyProvider : keyProvider ,
@@ -49,44 +60,60 @@ func NewReprovider(ctx context.Context, reprovideIniterval time.Duration, rsys r
49
60
50
61
// Close the reprovider
51
62
func (rp * Reprovider ) Close () error {
63
+ rp .cancel ()
64
+ <- rp .closedCh
52
65
return nil
53
66
}
54
67
55
68
// Run re-provides keys with 'tick' interval or when triggered
56
69
func (rp * Reprovider ) Run () {
57
- // dont reprovide immediately.
58
- // may have just started the daemon and shutting it down immediately.
59
- // probability( up another minute | uptime ) increases with uptime.
60
- after := time .After (time .Minute )
61
- var done doneFunc
62
- for {
63
- if rp .tick == 0 {
64
- after = make (chan time.Time )
70
+ defer close (rp .closedCh )
71
+
72
+ var initialReprovideCh , reprovideCh <- chan time.Time
73
+
74
+ // If reproviding is enabled (non-zero)
75
+ if rp .tick > 0 {
76
+ reprovideTicker := time .NewTicker (rp .tick )
77
+ defer reprovideTicker .Stop ()
78
+ reprovideCh = reprovideTicker .C
79
+
80
+ // If the reprovide ticker is larger than a minute (likely),
81
+ // provide once after we've been up a minute.
82
+ //
83
+ // Don't provide _immediately_ as we might be just about to stop.
84
+ if rp .tick > time .Minute {
85
+ initialReprovideTimer := time .NewTimer (time .Minute )
86
+ defer initialReprovideTimer .Stop ()
87
+
88
+ initialReprovideCh = initialReprovideTimer .C
65
89
}
90
+ }
66
91
92
+ var done chan <- error
93
+ for rp .ctx .Err () == nil {
67
94
select {
95
+ case <- initialReprovideCh :
96
+ case <- reprovideCh :
97
+ case done = <- rp .trigger :
68
98
case <- rp .ctx .Done ():
69
99
return
70
- case done = <- rp .trigger :
71
- case <- after :
72
100
}
73
101
74
- //'mute' the trigger channel so when `ipfs bitswap reprovide` is called
75
- //a 'reprovider is already running' error is returned
76
- unmute := rp .muteTrigger ()
77
-
78
102
err := rp .Reprovide ()
79
- if err != nil {
103
+
104
+ // only log if we've hit an actual error, otherwise just tell the client we're shutting down
105
+ if rp .ctx .Err () != nil {
106
+ err = ErrClosed
107
+ } else if err != nil {
80
108
logR .Errorf ("failed to reprovide: %s" , err )
81
109
}
82
110
83
111
if done != nil {
84
- done (err )
112
+ if err != nil {
113
+ done <- err
114
+ }
115
+ close (done )
85
116
}
86
-
87
- unmute ()
88
-
89
- after = time .After (rp .tick )
90
117
}
91
118
}
92
119
@@ -119,44 +146,27 @@ func (rp *Reprovider) Reprovide() error {
119
146
return nil
120
147
}
121
148
122
- // Trigger starts reprovision process in rp.Run and waits for it
149
+ // Trigger starts the reprovision process in rp.Run and waits for it to finish.
150
+ //
151
+ // Returns an error if a reprovide is already in progress.
123
152
func (rp * Reprovider ) Trigger (ctx context.Context ) error {
124
- progressCtx , done := context .WithCancel (ctx )
125
-
126
- var err error
127
- df := func (e error ) {
128
- err = e
129
- done ()
153
+ resultCh := make (chan error , 1 )
154
+ select {
155
+ case rp .trigger <- resultCh :
156
+ default :
157
+ return fmt .Errorf ("reprovider is already running" )
130
158
}
131
159
132
160
select {
161
+ case err := <- resultCh :
162
+ return err
133
163
case <- rp .ctx .Done ():
134
- return context . Canceled
164
+ return ErrClosed
135
165
case <- ctx .Done ():
136
- return context .Canceled
137
- case rp .trigger <- df :
138
- <- progressCtx .Done ()
139
- return err
166
+ return ctx .Err ()
140
167
}
141
168
}
142
169
143
- func (rp * Reprovider ) muteTrigger () context.CancelFunc {
144
- ctx , cf := context .WithCancel (rp .ctx )
145
- go func () {
146
- defer cf ()
147
- for {
148
- select {
149
- case <- ctx .Done ():
150
- return
151
- case done := <- rp .trigger :
152
- done (fmt .Errorf ("reprovider is already running" ))
153
- }
154
- }
155
- }()
156
-
157
- return cf
158
- }
159
-
160
170
// Strategies
161
171
162
172
// NewBlockstoreProvider returns key provider using bstore.AllKeysChan
0 commit comments