github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/old/netchan/common.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 "encoding/gob" 9 "errors" 10 "io" 11 "reflect" 12 "sync" 13 "time" 14 ) 15 16 // The direction of a connection from the client's perspective. 17 type Dir int 18 19 const ( 20 Recv Dir = iota 21 Send 22 ) 23 24 func (dir Dir) String() string { 25 switch dir { 26 case Recv: 27 return "Recv" 28 case Send: 29 return "Send" 30 } 31 return "???" 32 } 33 34 // Payload types 35 const ( 36 payRequest = iota // request structure follows 37 payError // error structure follows 38 payData // user payload follows 39 payAck // acknowledgement; no payload 40 payClosed // channel is now closed 41 payAckSend // payload has been delivered. 42 ) 43 44 // A header is sent as a prefix to every transmission. It will be followed by 45 // a request structure, an error structure, or an arbitrary user payload structure. 46 type header struct { 47 Id int 48 PayloadType int 49 SeqNum int64 50 } 51 52 // Sent with a header once per channel from importer to exporter to report 53 // that it wants to bind to a channel with the specified direction for count 54 // messages, with space for size buffered values. If count is -1, it means unlimited. 55 type request struct { 56 Name string 57 Count int64 58 Size int 59 Dir Dir 60 } 61 62 // Sent with a header to report an error. 63 type error_ struct { 64 Error string 65 } 66 67 // Used to unify management of acknowledgements for import and export. 68 type unackedCounter interface { 69 unackedCount() int64 70 ack() int64 71 seq() int64 72 } 73 74 // A channel and its direction. 75 type chanDir struct { 76 ch reflect.Value 77 dir Dir 78 } 79 80 // clientSet contains the objects and methods needed for tracking 81 // clients of an exporter and draining outstanding messages. 82 type clientSet struct { 83 mu sync.Mutex // protects access to channel and client maps 84 names map[string]*chanDir 85 clients map[unackedCounter]bool 86 } 87 88 // Mutex-protected encoder and decoder pair. 89 type encDec struct { 90 decLock sync.Mutex 91 dec *gob.Decoder 92 encLock sync.Mutex 93 enc *gob.Encoder 94 } 95 96 func newEncDec(conn io.ReadWriter) *encDec { 97 return &encDec{ 98 dec: gob.NewDecoder(conn), 99 enc: gob.NewEncoder(conn), 100 } 101 } 102 103 // Decode an item from the connection. 104 func (ed *encDec) decode(value reflect.Value) error { 105 ed.decLock.Lock() 106 err := ed.dec.DecodeValue(value) 107 if err != nil { 108 // TODO: tear down connection? 109 } 110 ed.decLock.Unlock() 111 return err 112 } 113 114 // Encode a header and payload onto the connection. 115 func (ed *encDec) encode(hdr *header, payloadType int, payload interface{}) error { 116 ed.encLock.Lock() 117 hdr.PayloadType = payloadType 118 err := ed.enc.Encode(hdr) 119 if err == nil { 120 if payload != nil { 121 err = ed.enc.Encode(payload) 122 } 123 } 124 if err != nil { 125 // TODO: tear down connection if there is an error? 126 } 127 ed.encLock.Unlock() 128 return err 129 } 130 131 // See the comment for Exporter.Drain. 132 func (cs *clientSet) drain(timeout time.Duration) error { 133 deadline := time.Now().Add(timeout) 134 for { 135 pending := false 136 cs.mu.Lock() 137 // Any messages waiting for a client? 138 for _, chDir := range cs.names { 139 if chDir.ch.Len() > 0 { 140 pending = true 141 } 142 } 143 // Any unacknowledged messages? 144 for client := range cs.clients { 145 n := client.unackedCount() 146 if n > 0 { // Check for > rather than != just to be safe. 147 pending = true 148 break 149 } 150 } 151 cs.mu.Unlock() 152 if !pending { 153 break 154 } 155 if timeout > 0 && time.Now().After(deadline) { 156 return errors.New("timeout") 157 } 158 time.Sleep(100 * time.Millisecond) 159 } 160 return nil 161 } 162 163 // See the comment for Exporter.Sync. 164 func (cs *clientSet) sync(timeout time.Duration) error { 165 deadline := time.Now().Add(timeout) 166 // seq remembers the clients and their seqNum at point of entry. 167 seq := make(map[unackedCounter]int64) 168 cs.mu.Lock() 169 for client := range cs.clients { 170 seq[client] = client.seq() 171 } 172 cs.mu.Unlock() 173 for { 174 pending := false 175 cs.mu.Lock() 176 // Any unacknowledged messages? Look only at clients that existed 177 // when we started and are still in this client set. 178 for client := range seq { 179 if _, ok := cs.clients[client]; ok { 180 if client.ack() < seq[client] { 181 pending = true 182 break 183 } 184 } 185 } 186 cs.mu.Unlock() 187 if !pending { 188 break 189 } 190 if timeout > 0 && time.Now().After(deadline) { 191 return errors.New("timeout") 192 } 193 time.Sleep(100 * time.Millisecond) 194 } 195 return nil 196 } 197 198 // A netChan represents a channel imported or exported 199 // on a single connection. Flow is controlled by the receiving 200 // side by sending payAckSend messages when values 201 // are delivered into the local channel. 202 type netChan struct { 203 *chanDir 204 name string 205 id int 206 size int // buffer size of channel. 207 closed bool 208 209 // sender-specific state 210 ackCh chan bool // buffered with space for all the acks we need 211 space int // available space. 212 213 // receiver-specific state 214 sendCh chan reflect.Value // buffered channel of values received from other end. 215 ed *encDec // so that we can send acks. 216 count int64 // number of values still to receive. 217 } 218 219 // Create a new netChan with the given name (only used for 220 // messages), id, direction, buffer size, and count. 221 // The connection to the other side is represented by ed. 222 func newNetChan(name string, id int, ch *chanDir, ed *encDec, size int, count int64) *netChan { 223 c := &netChan{chanDir: ch, name: name, id: id, size: size, ed: ed, count: count} 224 if c.dir == Send { 225 c.ackCh = make(chan bool, size) 226 c.space = size 227 } 228 return c 229 } 230 231 // Close the channel. 232 func (nch *netChan) close() { 233 if nch.closed { 234 return 235 } 236 if nch.dir == Recv { 237 if nch.sendCh != nil { 238 // If the sender goroutine is active, close the channel to it. 239 // It will close nch.ch when it can. 240 close(nch.sendCh) 241 } else { 242 nch.ch.Close() 243 } 244 } else { 245 nch.ch.Close() 246 close(nch.ackCh) 247 } 248 nch.closed = true 249 } 250 251 // Send message from remote side to local receiver. 252 func (nch *netChan) send(val reflect.Value) { 253 if nch.dir != Recv { 254 panic("send on wrong direction of channel") 255 } 256 if nch.sendCh == nil { 257 // If possible, do local send directly and ack immediately. 258 if nch.ch.TrySend(val) { 259 nch.sendAck() 260 return 261 } 262 // Start sender goroutine to manage delayed delivery of values. 263 nch.sendCh = make(chan reflect.Value, nch.size) 264 go nch.sender() 265 } 266 select { 267 case nch.sendCh <- val: 268 // ok 269 default: 270 // TODO: should this be more resilient? 271 panic("netchan: remote sender sent more values than allowed") 272 } 273 } 274 275 // sendAck sends an acknowledgment that a message has left 276 // the channel's buffer. If the messages remaining to be sent 277 // will fit in the channel's buffer, then we don't 278 // need to send an ack. 279 func (nch *netChan) sendAck() { 280 if nch.count < 0 || nch.count > int64(nch.size) { 281 nch.ed.encode(&header{Id: nch.id}, payAckSend, nil) 282 } 283 if nch.count > 0 { 284 nch.count-- 285 } 286 } 287 288 // The sender process forwards items from the sending queue 289 // to the destination channel, acknowledging each item. 290 func (nch *netChan) sender() { 291 if nch.dir != Recv { 292 panic("sender on wrong direction of channel") 293 } 294 // When Exporter.Hangup is called, the underlying channel is closed, 295 // and so we may get a "too many operations on closed channel" error 296 // if there are outstanding messages in sendCh. 297 // Make sure that this doesn't panic the whole program. 298 defer func() { 299 if r := recover(); r != nil { 300 // TODO check that r is "too many operations", otherwise re-panic. 301 } 302 }() 303 for v := range nch.sendCh { 304 nch.ch.Send(v) 305 nch.sendAck() 306 } 307 nch.ch.Close() 308 } 309 310 // Receive value from local side for sending to remote side. 311 func (nch *netChan) recv() (val reflect.Value, ok bool) { 312 if nch.dir != Send { 313 panic("recv on wrong direction of channel") 314 } 315 316 if nch.space == 0 { 317 // Wait for buffer space. 318 <-nch.ackCh 319 nch.space++ 320 } 321 nch.space-- 322 return nch.ch.Recv() 323 } 324 325 // acked is called when the remote side indicates that 326 // a value has been delivered. 327 func (nch *netChan) acked() { 328 if nch.dir != Send { 329 panic("recv on wrong direction of channel") 330 } 331 select { 332 case nch.ackCh <- true: 333 // ok 334 default: 335 // TODO: should this be more resilient? 336 panic("netchan: remote receiver sent too many acks") 337 } 338 }