@@ -276,6 +276,8 @@ type Cacher struct {
276
276
// watchersBuffer is a list of watchers potentially interested in currently
277
277
// dispatched event.
278
278
watchersBuffer []* cacheWatcher
279
+ // blockedWatchers is a list of watchers whose buffer is currently full.
280
+ blockedWatchers []* cacheWatcher
279
281
// watchersToStop is a list of watchers that were supposed to be stopped
280
282
// during current dispatching, but stopping was deferred to the end of
281
283
// dispatching that event to avoid race with closing channels in watchers.
@@ -789,13 +791,45 @@ func (c *Cacher) dispatchEvent(event *watchCacheEvent) {
789
791
// Watchers stopped after startDispatching will be delayed to finishDispatching,
790
792
791
793
// Since add() can block, we explicitly add when cacher is unlocked.
794
+ // Dispatching event in nonblocking way first, which make faster watchers
795
+ // not be blocked by slower ones.
792
796
if event .Type == watch .Bookmark {
793
797
for _ , watcher := range c .watchersBuffer {
794
798
watcher .nonblockingAdd (event )
795
799
}
796
800
} else {
801
+ c .blockedWatchers = c .blockedWatchers [:0 ]
797
802
for _ , watcher := range c .watchersBuffer {
798
- watcher .add (event , c .timer , c .dispatchTimeoutBudget )
803
+ if ! watcher .nonblockingAdd (event ) {
804
+ c .blockedWatchers = append (c .blockedWatchers , watcher )
805
+ }
806
+ }
807
+
808
+ if len (c .blockedWatchers ) > 0 {
809
+ // dispatchEvent is called very often, so arrange
810
+ // to reuse timers instead of constantly allocating.
811
+ startTime := time .Now ()
812
+ timeout := c .dispatchTimeoutBudget .takeAvailable ()
813
+ c .timer .Reset (timeout )
814
+
815
+ // Make sure every watcher will try to send event without blocking first,
816
+ // even if the timer has already expired.
817
+ timer := c .timer
818
+ for _ , watcher := range c .blockedWatchers {
819
+ if ! watcher .add (event , timer ) {
820
+ // fired, clean the timer by set it to nil.
821
+ timer = nil
822
+ }
823
+ }
824
+
825
+ // Stop the timer if it is not fired
826
+ if timer != nil && ! timer .Stop () {
827
+ // Consume triggered (but not yet received) timer event
828
+ // so that future reuse does not get a spurious timeout.
829
+ <- timer .C
830
+ }
831
+
832
+ c .dispatchTimeoutBudget .returnUnused (timeout - time .Since (startTime ))
799
833
}
800
834
}
801
835
}
@@ -1078,7 +1112,6 @@ func (c *cacheWatcher) stop() {
1078
1112
}
1079
1113
1080
1114
func (c * cacheWatcher ) nonblockingAdd (event * watchCacheEvent ) bool {
1081
- // If we can't send it, don't block on it.
1082
1115
select {
1083
1116
case c .input <- event :
1084
1117
return true
@@ -1087,36 +1120,34 @@ func (c *cacheWatcher) nonblockingAdd(event *watchCacheEvent) bool {
1087
1120
}
1088
1121
}
1089
1122
1090
- func (c * cacheWatcher ) add (event * watchCacheEvent , timer * time.Timer , budget * timeBudget ) {
1123
+ // Nil timer means that add will not block (if it can't send event immediately, it will break the watcher)
1124
+ func (c * cacheWatcher ) add (event * watchCacheEvent , timer * time.Timer ) bool {
1091
1125
// Try to send the event immediately, without blocking.
1092
1126
if c .nonblockingAdd (event ) {
1093
- return
1127
+ return true
1094
1128
}
1095
1129
1096
- // OK, block sending, but only for up to <timeout>.
1097
- // cacheWatcher.add is called very often, so arrange
1098
- // to reuse timers instead of constantly allocating.
1099
- startTime := time .Now ()
1100
- timeout := budget .takeAvailable ()
1101
-
1102
- timer .Reset (timeout )
1103
-
1104
- select {
1105
- case c .input <- event :
1106
- if ! timer .Stop () {
1107
- // Consume triggered (but not yet received) timer event
1108
- // so that future reuse does not get a spurious timeout.
1109
- <- timer .C
1110
- }
1111
- case <- timer .C :
1130
+ closeFunc := func () {
1112
1131
// This means that we couldn't send event to that watcher.
1113
1132
// Since we don't want to block on it infinitely,
1114
1133
// we simply terminate it.
1115
1134
klog .V (1 ).Infof ("Forcing watcher close due to unresponsiveness: %v" , reflect .TypeOf (event .Object ).String ())
1116
1135
c .forget ()
1117
1136
}
1118
1137
1119
- budget .returnUnused (timeout - time .Since (startTime ))
1138
+ if timer == nil {
1139
+ closeFunc ()
1140
+ return false
1141
+ }
1142
+
1143
+ // OK, block sending, but only until timer fires.
1144
+ select {
1145
+ case c .input <- event :
1146
+ return true
1147
+ case <- timer .C :
1148
+ closeFunc ()
1149
+ return false
1150
+ }
1120
1151
}
1121
1152
1122
1153
func (c * cacheWatcher ) nextBookmarkTime (now time.Time ) (time.Time , bool ) {
0 commit comments