@@ -15,6 +15,7 @@ import (
15
15
"path/filepath"
16
16
"strconv"
17
17
"strings"
18
+ "sync"
18
19
"testing"
19
20
"time"
20
21
@@ -23,11 +24,8 @@ import (
23
24
)
24
25
25
26
type Client struct {
26
- url string
27
- // Cache of module paths already seen.
28
- seen map [string ]bool
29
- // Does seen contain all known modules?
30
- cacheComplete bool
27
+ url string
28
+ cache * cache
31
29
}
32
30
33
31
func Default () * Client {
@@ -36,17 +34,13 @@ func Default() *Client {
36
34
37
35
func New (url string ) * Client {
38
36
return & Client {
39
- url : url ,
40
- seen : make (map [string ]bool ),
41
- cacheComplete : false ,
37
+ url : url ,
38
+ cache : newCache (),
42
39
}
43
40
}
44
41
45
42
func (pc * Client ) SetKnownModules (known []string ) {
46
- for _ , km := range known {
47
- pc .seen [km ] = true
48
- }
49
- pc .cacheComplete = true
43
+ pc .cache .setKnownModules (known )
50
44
}
51
45
52
46
// Limit pkgsite requests to this many per second.
@@ -64,13 +58,11 @@ var pkgsiteURL = "https://pkg.go.dev"
64
58
// Known reports whether pkgsite knows that modulePath actually refers
65
59
// to a module.
66
60
func (pc * Client ) Known (ctx context.Context , modulePath string ) (bool , error ) {
67
- // If we've seen it before, no need to call.
68
- if b , ok := pc .seen [modulePath ]; ok {
69
- return b , nil
70
- }
71
- if pc .cacheComplete {
72
- return false , nil
61
+ found , ok := pc .cache .lookup (modulePath )
62
+ if ok {
63
+ return found , nil
73
64
}
65
+
74
66
// Pause to maintain a max QPS.
75
67
if err := pkgsiteRateLimiter .Wait (ctx ); err != nil {
76
68
return false , err
@@ -92,7 +84,7 @@ func (pc *Client) Known(ctx context.Context, modulePath string) (bool, error) {
92
84
return false , err
93
85
}
94
86
known := res .StatusCode == http .StatusOK
95
- pc .seen [ modulePath ] = known
87
+ pc .cache . add ( modulePath , known )
96
88
return known , nil
97
89
}
98
90
@@ -115,7 +107,10 @@ func readKnown(r io.Reader) (map[string]bool, error) {
115
107
return seen , nil
116
108
}
117
109
118
- func (c * Client ) writeKnown (w io.Writer ) error {
110
+ func (c * cache ) writeKnown (w io.Writer ) error {
111
+ c .mu .Lock ()
112
+ defer c .mu .Unlock ()
113
+
119
114
b , err := json .MarshalIndent (c .seen , "" , " " )
120
115
if err != nil {
121
116
return err
@@ -164,7 +159,7 @@ func TestClient(t *testing.T, useRealPkgsite bool, rw io.ReadWriter) (*Client, e
164
159
if useRealPkgsite {
165
160
c := Default ()
166
161
t .Cleanup (func () {
167
- err := c .writeKnown (rw )
162
+ err := c .cache . writeKnown (rw )
168
163
if err != nil {
169
164
t .Error (err )
170
165
}
@@ -184,3 +179,54 @@ func TestClient(t *testing.T, useRealPkgsite bool, rw io.ReadWriter) (*Client, e
184
179
t .Cleanup (s .Close )
185
180
return New (s .URL ), nil
186
181
}
182
+
183
+ type cache struct {
184
+ mu sync.Mutex
185
+ // Module paths already seen.
186
+ seen map [string ]bool
187
+ // Does the cache contain all known modules?
188
+ complete bool
189
+ }
190
+
191
+ func newCache () * cache {
192
+ return & cache {
193
+ seen : make (map [string ]bool ),
194
+ complete : false ,
195
+ }
196
+ }
197
+
198
+ func (c * cache ) setKnownModules (known []string ) {
199
+ c .mu .Lock ()
200
+ defer c .mu .Unlock ()
201
+
202
+ for _ , km := range known {
203
+ c .seen [km ] = true
204
+ }
205
+ c .complete = true
206
+ }
207
+
208
+ func (c * cache ) lookup (modulePath string ) (known bool , ok bool ) {
209
+ c .mu .Lock ()
210
+ defer c .mu .Unlock ()
211
+
212
+ // In the cache.
213
+ if known , ok := c .seen [modulePath ]; ok {
214
+ return known , true
215
+ }
216
+
217
+ // Not in the cache, but the cache is complete, so this
218
+ // module is not known.
219
+ if c .complete {
220
+ return false , true
221
+ }
222
+
223
+ // We can't make a statement about this module.
224
+ return false , false
225
+ }
226
+
227
+ func (c * cache ) add (modulePath string , known bool ) {
228
+ c .mu .Lock ()
229
+ defer c .mu .Unlock ()
230
+
231
+ c .seen [modulePath ] = known
232
+ }
0 commit comments