github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/old/netchan/import.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package netchan 6 7 import ( 8 "errors" 9 "io" 10 "log" 11 "net" 12 "reflect" 13 "sync" 14 "time" 15 ) 16 17 // Import 18 19 // impLog is a logging convenience function. The first argument must be a string. 20 func impLog(args ...interface{}) { 21 args[0] = "netchan import: " + args[0].(string) 22 log.Print(args...) 23 } 24 25 // An Importer allows a set of channels to be imported from a single 26 // remote machine/network port. A machine may have multiple 27 // importers, even from the same machine/network port. 28 type Importer struct { 29 *encDec 30 chanLock sync.Mutex // protects access to channel map 31 names map[string]*netChan 32 chans map[int]*netChan 33 errors chan error 34 maxId int 35 mu sync.Mutex // protects remaining fields 36 unacked int64 // number of unacknowledged sends. 37 seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu 38 } 39 40 // NewImporter creates a new Importer object to import a set of channels 41 // from the given connection. The Exporter must be available and serving when 42 // the Importer is created. 43 func NewImporter(conn io.ReadWriter) *Importer { 44 imp := new(Importer) 45 imp.encDec = newEncDec(conn) 46 imp.chans = make(map[int]*netChan) 47 imp.names = make(map[string]*netChan) 48 imp.errors = make(chan error, 10) 49 imp.unacked = 0 50 go imp.run() 51 return imp 52 } 53 54 // Import imports a set of channels from the given network and address. 55 func Import(network, remoteaddr string) (*Importer, error) { 56 conn, err := net.Dial(network, remoteaddr) 57 if err != nil { 58 return nil, err 59 } 60 return NewImporter(conn), nil 61 } 62 63 // shutdown closes all channels for which we are receiving data from the remote side. 64 func (imp *Importer) shutdown() { 65 imp.chanLock.Lock() 66 for _, ich := range imp.chans { 67 if ich.dir == Recv { 68 ich.close() 69 } 70 } 71 imp.chanLock.Unlock() 72 } 73 74 // Handle the data from a single imported data stream, which will 75 // have the form 76 // (response, data)* 77 // The response identifies by name which channel is transmitting data. 78 func (imp *Importer) run() { 79 // Loop on responses; requests are sent by ImportNValues() 80 hdr := new(header) 81 hdrValue := reflect.ValueOf(hdr) 82 ackHdr := new(header) 83 err := new(error_) 84 errValue := reflect.ValueOf(err) 85 for { 86 *hdr = header{} 87 if e := imp.decode(hdrValue); e != nil { 88 if e != io.EOF { 89 impLog("header:", e) 90 imp.shutdown() 91 } 92 return 93 } 94 switch hdr.PayloadType { 95 case payData: 96 // done lower in loop 97 case payError: 98 if e := imp.decode(errValue); e != nil { 99 impLog("error:", e) 100 return 101 } 102 if err.Error != "" { 103 impLog("response error:", err.Error) 104 select { 105 case imp.errors <- errors.New(err.Error): 106 continue // errors are not acknowledged 107 default: 108 imp.shutdown() 109 return 110 } 111 } 112 case payClosed: 113 nch := imp.getChan(hdr.Id, false) 114 if nch != nil { 115 nch.close() 116 } 117 continue // closes are not acknowledged. 118 case payAckSend: 119 // we can receive spurious acks if the channel is 120 // hung up, so we ask getChan to ignore any errors. 121 nch := imp.getChan(hdr.Id, true) 122 if nch != nil { 123 nch.acked() 124 imp.mu.Lock() 125 imp.unacked-- 126 imp.mu.Unlock() 127 } 128 continue 129 default: 130 impLog("unexpected payload type:", hdr.PayloadType) 131 return 132 } 133 nch := imp.getChan(hdr.Id, false) 134 if nch == nil { 135 continue 136 } 137 if nch.dir != Recv { 138 impLog("cannot happen: receive from non-Recv channel") 139 return 140 } 141 // Acknowledge receipt 142 ackHdr.Id = hdr.Id 143 ackHdr.SeqNum = hdr.SeqNum 144 imp.encode(ackHdr, payAck, nil) 145 // Create a new value for each received item. 146 value := reflect.New(nch.ch.Type().Elem()).Elem() 147 if e := imp.decode(value); e != nil { 148 impLog("importer value decode:", e) 149 return 150 } 151 nch.send(value) 152 } 153 } 154 155 func (imp *Importer) getChan(id int, errOk bool) *netChan { 156 imp.chanLock.Lock() 157 ich := imp.chans[id] 158 imp.chanLock.Unlock() 159 if ich == nil { 160 if !errOk { 161 impLog("unknown id in netchan request: ", id) 162 } 163 return nil 164 } 165 return ich 166 } 167 168 // Errors returns a channel from which transmission and protocol errors 169 // can be read. Clients of the importer are not required to read the error 170 // channel for correct execution. However, if too many errors occur 171 // without being read from the error channel, the importer will shut down. 172 func (imp *Importer) Errors() chan error { 173 return imp.errors 174 } 175 176 // Import imports a channel of the given type, size and specified direction. 177 // It is equivalent to ImportNValues with a count of -1, meaning unbounded. 178 func (imp *Importer) Import(name string, chT interface{}, dir Dir, size int) error { 179 return imp.ImportNValues(name, chT, dir, size, -1) 180 } 181 182 // ImportNValues imports a channel of the given type and specified 183 // direction and then receives or transmits up to n values on that 184 // channel. A value of n==-1 implies an unbounded number of values. The 185 // channel will have buffer space for size values, or 1 value if size < 1. 186 // The channel to be bound to the remote site's channel is provided 187 // in the call and may be of arbitrary channel type. 188 // Despite the literal signature, the effective signature is 189 // ImportNValues(name string, chT chan T, dir Dir, size, n int) error 190 // Example usage: 191 // imp, err := NewImporter("tcp", "netchanserver.mydomain.com:1234") 192 // if err != nil { log.Fatal(err) } 193 // ch := make(chan myType) 194 // err = imp.ImportNValues("name", ch, Recv, 1, 1) 195 // if err != nil { log.Fatal(err) } 196 // fmt.Printf("%+v\n", <-ch) 197 func (imp *Importer) ImportNValues(name string, chT interface{}, dir Dir, size, n int) error { 198 ch, err := checkChan(chT, dir) 199 if err != nil { 200 return err 201 } 202 imp.chanLock.Lock() 203 defer imp.chanLock.Unlock() 204 _, present := imp.names[name] 205 if present { 206 return errors.New("channel name already being imported:" + name) 207 } 208 if size < 1 { 209 size = 1 210 } 211 id := imp.maxId 212 imp.maxId++ 213 nch := newNetChan(name, id, &chanDir{ch, dir}, imp.encDec, size, int64(n)) 214 imp.names[name] = nch 215 imp.chans[id] = nch 216 // Tell the other side about this channel. 217 hdr := &header{Id: id} 218 req := &request{Name: name, Count: int64(n), Dir: dir, Size: size} 219 if err = imp.encode(hdr, payRequest, req); err != nil { 220 impLog("request encode:", err) 221 return err 222 } 223 if dir == Send { 224 go func() { 225 for i := 0; n == -1 || i < n; i++ { 226 val, ok := nch.recv() 227 if !ok { 228 if err = imp.encode(hdr, payClosed, nil); err != nil { 229 impLog("error encoding client closed message:", err) 230 } 231 return 232 } 233 // We hold the lock during transmission to guarantee messages are 234 // sent in order. 235 imp.mu.Lock() 236 imp.unacked++ 237 imp.seqLock.Lock() 238 imp.mu.Unlock() 239 if err = imp.encode(hdr, payData, val.Interface()); err != nil { 240 impLog("error encoding client send:", err) 241 return 242 } 243 imp.seqLock.Unlock() 244 } 245 }() 246 } 247 return nil 248 } 249 250 // Hangup disassociates the named channel from the Importer and closes 251 // the channel. Messages in flight for the channel may be dropped. 252 func (imp *Importer) Hangup(name string) error { 253 imp.chanLock.Lock() 254 defer imp.chanLock.Unlock() 255 nc := imp.names[name] 256 if nc == nil { 257 return errors.New("netchan import: hangup: no such channel: " + name) 258 } 259 delete(imp.names, name) 260 delete(imp.chans, nc.id) 261 nc.close() 262 return nil 263 } 264 265 func (imp *Importer) unackedCount() int64 { 266 imp.mu.Lock() 267 n := imp.unacked 268 imp.mu.Unlock() 269 return n 270 } 271 272 // Drain waits until all messages sent from this exporter/importer, including 273 // those not yet sent to any server and possibly including those sent while 274 // Drain was executing, have been received by the exporter. In short, it 275 // waits until all the importer's messages have been received. 276 // If the timeout (measured in nanoseconds) is positive and Drain takes 277 // longer than that to complete, an error is returned. 278 func (imp *Importer) Drain(timeout int64) error { 279 deadline := time.Now().Add(time.Duration(timeout)) 280 for imp.unackedCount() > 0 { 281 if timeout > 0 && time.Now().After(deadline) { 282 return errors.New("timeout") 283 } 284 time.Sleep(100 * time.Millisecond) 285 } 286 return nil 287 }