// receive reads result from the decoder in a loop and sends down the result channel. func (sw *StreamWatcher) receive() { defer close(sw.result) defer sw.Stop() defer utilruntime.HandleCrash() for { action, obj, err := sw.source.Decode() if err != nil { // Ignore expected error. if sw.stopping() { return } switch err { case io.EOF: // watch closed normally case io.ErrUnexpectedEOF: glog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err) default: msg := "Unable to decode an event from the watch stream: %v" if net.IsProbableEOF(err) { glog.V(5).Infof(msg, err) } else { glog.Errorf(msg, err) } } return } sw.result <- Event{ Type: action, Object: obj, } } }
// Until loops until stop channel is closed, running f every period. // Catches any panics, and keeps going. f may not be invoked if // stop channel is already closed. Pass NeverStop to Until if you // don't want it stop. func Until(f func(), period time.Duration, stopCh <-chan struct{}) { select { case <-stopCh: return default: } for { func() { defer runtime.HandleCrash() f() }() select { case <-stopCh: return case <-time.After(period): } } }
// getOrExpire retrieves the object from the timestampedEntry if and only if it hasn't // already expired. It kicks-off a go routine to delete expired objects from // the store and sets exists=false. func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) { timestampedItem, exists := c.getTimestampedEntry(key) if !exists { return nil, false } if c.expirationPolicy.IsExpired(timestampedItem) { glog.V(4).Infof("Entry %v: %+v has expired", key, timestampedItem.obj) // Since expiration happens lazily on read, don't hold up // the reader trying to acquire a write lock for the delete. // The next reader will retry the delete even if this one // fails; as long as we only return un-expired entries a // reader doesn't need to wait for the result of the delete. go func() { defer runtime.HandleCrash() c.cacheStorage.Delete(key) }() return nil, false } return timestampedItem.obj, true }