53
53
},
54
54
[]string {"resource" },
55
55
)
56
+ emptyFunc = func () {}
56
57
)
57
58
58
59
func init () {
@@ -339,21 +340,6 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
339
340
340
341
c .ready .wait ()
341
342
342
- // We explicitly use thread unsafe version and do locking ourself to ensure that
343
- // no new events will be processed in the meantime. The watchCache will be unlocked
344
- // on return from this function.
345
- // Note that we cannot do it under Cacher lock, to avoid a deadlock, since the
346
- // underlying watchCache is calling processEvent under its lock.
347
- c .watchCache .RLock ()
348
- defer c .watchCache .RUnlock ()
349
- initEvents , err := c .watchCache .GetAllEventsSinceThreadUnsafe (watchRV )
350
- if err != nil {
351
- // To match the uncached watch implementation, once we have passed authn/authz/admission,
352
- // and successfully parsed a resource version, other errors must fail with a watch event of type ERROR,
353
- // rather than a directly returned error.
354
- return newErrWatcher (err ), nil
355
- }
356
-
357
343
triggerValue , triggerSupported := "" , false
358
344
// TODO: Currently we assume that in a given Cacher object, any <predicate> that is
359
345
// passed here is aware of exactly the same trigger (at most one).
@@ -377,20 +363,42 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
377
363
chanSize = 1000
378
364
}
379
365
366
+ // Create a watcher here to reduce memory allocations under lock,
367
+ // given that memory allocation may trigger GC and block the thread.
368
+ watcher := newCacheWatcher (chanSize , filterWithAttrsFunction (key , pred ), emptyFunc , c .versioner )
369
+
370
+ // We explicitly use thread unsafe version and do locking ourself to ensure that
371
+ // no new events will be processed in the meantime. The watchCache will be unlocked
372
+ // on return from this function.
373
+ // Note that we cannot do it under Cacher lock, to avoid a deadlock, since the
374
+ // underlying watchCache is calling processEvent under its lock.
375
+ c .watchCache .RLock ()
376
+ defer c .watchCache .RUnlock ()
377
+ initEvents , err := c .watchCache .GetAllEventsSinceThreadUnsafe (watchRV )
378
+ if err != nil {
379
+ // To match the uncached watch implementation, once we have passed authn/authz/admission,
380
+ // and successfully parsed a resource version, other errors must fail with a watch event of type ERROR,
381
+ // rather than a directly returned error.
382
+ return newErrWatcher (err ), nil
383
+ }
384
+
380
385
// With some events already sent, update resourceVersion so that
381
386
// events that were buffered and not yet processed won't be delivered
382
387
// to this watcher second time causing going back in time.
383
388
if len (initEvents ) > 0 {
384
389
watchRV = initEvents [len (initEvents )- 1 ].ResourceVersion
385
390
}
386
391
387
- c .Lock ()
388
- defer c .Unlock ()
389
- forget := forgetWatcher (c , c .watcherIdx , triggerValue , triggerSupported )
390
- watcher := newCacheWatcher (watchRV , chanSize , initEvents , filterWithAttrsFunction (key , pred ), forget , c .versioner )
392
+ func () {
393
+ c .Lock ()
394
+ defer c .Unlock ()
395
+ // Update watcher.forget function once we can compute it.
396
+ watcher .forget = forgetWatcher (c , c .watcherIdx , triggerValue , triggerSupported )
397
+ c .watchers .addWatcher (watcher , c .watcherIdx , triggerValue , triggerSupported )
398
+ c .watcherIdx ++
399
+ }()
391
400
392
- c .watchers .addWatcher (watcher , c .watcherIdx , triggerValue , triggerSupported )
393
- c .watcherIdx ++
401
+ go watcher .process (initEvents , watchRV )
394
402
return watcher , nil
395
403
}
396
404
@@ -879,8 +887,8 @@ type cacheWatcher struct {
879
887
versioner storage.Versioner
880
888
}
881
889
882
- func newCacheWatcher (resourceVersion uint64 , chanSize int , initEvents [] * watchCacheEvent , filter filterWithAttrsFunc , forget func (), versioner storage.Versioner ) * cacheWatcher {
883
- watcher := & cacheWatcher {
890
+ func newCacheWatcher (chanSize int , filter filterWithAttrsFunc , forget func (), versioner storage.Versioner ) * cacheWatcher {
891
+ return & cacheWatcher {
884
892
input : make (chan * watchCacheEvent , chanSize ),
885
893
result : make (chan watch.Event , chanSize ),
886
894
done : make (chan struct {}),
@@ -889,8 +897,6 @@ func newCacheWatcher(resourceVersion uint64, chanSize int, initEvents []*watchCa
889
897
forget : forget ,
890
898
versioner : versioner ,
891
899
}
892
- go watcher .process (initEvents , resourceVersion )
893
- return watcher
894
900
}
895
901
896
902
// Implements watch.Interface.
0 commit comments