github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/iowatcher.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package watch 18 19 import ( 20 "io" 21 "sync" 22 23 "github.com/golang/glog" 24 "k8s.io/kubernetes/pkg/runtime" 25 "k8s.io/kubernetes/pkg/util" 26 ) 27 28 // Decoder allows StreamWatcher to watch any stream for which a Decoder can be written. 29 type Decoder interface { 30 // Decode should return the type of event, the decoded object, or an error. 31 // An error will cause StreamWatcher to call Close(). Decode should block until 32 // it has data or an error occurs. 33 Decode() (action EventType, object runtime.Object, err error) 34 35 // Close should close the underlying io.Reader, signalling to the source of 36 // the stream that it is no longer being watched. Close() must cause any 37 // outstanding call to Decode() to return with an error of some sort. 38 Close() 39 } 40 41 // StreamWatcher turns any stream for which you can write a Decoder interface 42 // into a watch.Interface. 43 type StreamWatcher struct { 44 source Decoder 45 result chan Event 46 sync.Mutex 47 stopped bool 48 } 49 50 // NewStreamWatcher creates a StreamWatcher from the given decoder. 51 func NewStreamWatcher(d Decoder) *StreamWatcher { 52 sw := &StreamWatcher{ 53 source: d, 54 // It's easy for a consumer to add buffering via an extra 55 // goroutine/channel, but impossible for them to remove it, 56 // so nonbuffered is better. 57 result: make(chan Event), 58 } 59 go sw.receive() 60 return sw 61 } 62 63 // ResultChan implements Interface. 64 func (sw *StreamWatcher) ResultChan() <-chan Event { 65 return sw.result 66 } 67 68 // Stop implements Interface. 69 func (sw *StreamWatcher) Stop() { 70 // Call Close() exactly once by locking and setting a flag. 71 sw.Lock() 72 defer sw.Unlock() 73 if !sw.stopped { 74 sw.stopped = true 75 sw.source.Close() 76 } 77 } 78 79 // stopping returns true if Stop() was called previously. 80 func (sw *StreamWatcher) stopping() bool { 81 sw.Lock() 82 defer sw.Unlock() 83 return sw.stopped 84 } 85 86 // receive reads result from the decoder in a loop and sends down the result channel. 87 func (sw *StreamWatcher) receive() { 88 defer close(sw.result) 89 defer sw.Stop() 90 defer util.HandleCrash() 91 for { 92 action, obj, err := sw.source.Decode() 93 if err != nil { 94 // Ignore expected error. 95 if sw.stopping() { 96 return 97 } 98 switch err { 99 case io.EOF: 100 // watch closed normally 101 case io.ErrUnexpectedEOF: 102 glog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err) 103 default: 104 msg := "Unable to decode an event from the watch stream: %v" 105 if util.IsProbableEOF(err) { 106 glog.V(5).Infof(msg, err) 107 } else { 108 glog.Errorf(msg, err) 109 } 110 } 111 return 112 } 113 sw.result <- Event{ 114 Type: action, 115 Object: obj, 116 } 117 } 118 }