github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/net/conn/multiconn.go (about) 1 package conn 2 3 import ( 4 "sync" 5 6 context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" 7 ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" 8 9 peer "github.com/jbenet/go-ipfs/peer" 10 u "github.com/jbenet/go-ipfs/util" 11 ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" 12 ) 13 14 // MultiConnMap is for shorthand 15 type MultiConnMap map[u.Key]*MultiConn 16 17 // Duplex is a simple duplex channel 18 type Duplex struct { 19 In chan []byte 20 Out chan []byte 21 } 22 23 // MultiConn represents a single connection to another Peer (IPFS Node). 24 type MultiConn struct { 25 26 // connections, mapped by a string, which uniquely identifies the connection. 27 // this string is: /addr1/peer1/addr2/peer2 (peers ordered lexicographically) 28 conns map[string]Conn 29 30 local peer.Peer 31 remote peer.Peer 32 33 // fan-in/fan-out 34 duplex Duplex 35 36 // for adding/removing connections concurrently 37 sync.RWMutex 38 ctxc.ContextCloser 39 } 40 41 // NewMultiConn constructs a new connection 42 func NewMultiConn(ctx context.Context, local, remote peer.Peer, conns []Conn) (*MultiConn, error) { 43 44 c := &MultiConn{ 45 local: local, 46 remote: remote, 47 conns: map[string]Conn{}, 48 duplex: Duplex{ 49 In: make(chan []byte, 10), 50 Out: make(chan []byte, 10), 51 }, 52 } 53 54 // must happen before Adds / fanOut 55 c.ContextCloser = ctxc.NewContextCloser(ctx, c.close) 56 57 if conns != nil && len(conns) > 0 { 58 c.Add(conns...) 59 } 60 61 c.Children().Add(1) 62 go c.fanOut() 63 return c, nil 64 } 65 66 // Add adds given Conn instances to multiconn. 67 func (c *MultiConn) Add(conns ...Conn) { 68 c.Lock() 69 defer c.Unlock() 70 71 for _, c2 := range conns { 72 log.Infof("MultiConn: adding %s", c2) 73 if c.LocalPeer() != c2.LocalPeer() || c.RemotePeer() != c2.RemotePeer() { 74 log.Error(c2) 75 c.Unlock() // ok to unlock (to log). panicing. 76 log.Error(c) 77 // log.Errorf("c.LocalPeer: %s %p", c.LocalPeer(), c.LocalPeer()) 78 // log.Errorf("c2.LocalPeer: %s %p", c2.LocalPeer(), c2.LocalPeer()) 79 // log.Errorf("c.RemotePeer: %s %p", c.RemotePeer(), c.RemotePeer()) 80 // log.Errorf("c2.RemotePeer: %s %p", c2.RemotePeer(), c2.RemotePeer()) 81 c.Lock() // gotta relock to avoid lock panic from deferring. 82 panic("connection addresses mismatch") 83 } 84 85 c.conns[c2.ID()] = c2 86 c.Children().Add(1) 87 c2.Children().Add(1) // yep, on the child too. 88 go c.fanInSingle(c2) 89 log.Infof("MultiConn: added %s", c2) 90 } 91 } 92 93 // Remove removes given Conn instances from multiconn. 94 func (c *MultiConn) Remove(conns ...Conn) { 95 96 // first remove them to avoid sending any more messages through it. 97 { 98 c.Lock() 99 for _, c1 := range conns { 100 c2, found := c.conns[c1.ID()] 101 if !found { 102 panic("Conn not in MultiConn") 103 } 104 if c1 != c2 { 105 panic("different Conn objects for same id.") 106 } 107 108 delete(c.conns, c2.ID()) 109 } 110 c.Unlock() 111 } 112 113 // close all in parallel, but wait for all to be done closing. 114 CloseConns(conns...) 115 } 116 117 // CloseConns closes multiple connections in parallel, and waits for all 118 // to finish closing. 119 func CloseConns(conns ...Conn) { 120 var wg sync.WaitGroup 121 for _, child := range conns { 122 123 select { 124 case <-child.Closed(): // if already closed, continue 125 continue 126 default: 127 } 128 129 wg.Add(1) 130 go func(child Conn) { 131 child.Close() 132 wg.Done() 133 }(child) 134 } 135 wg.Wait() 136 } 137 138 // fanOut is the multiplexor out -- it sends outgoing messages over the 139 // underlying single connections. 140 func (c *MultiConn) fanOut() { 141 defer c.Children().Done() 142 143 i := 0 144 for { 145 select { 146 case <-c.Closing(): 147 return 148 149 // send data out through our "best connection" 150 case m, more := <-c.duplex.Out: 151 if !more { 152 log.Infof("%s out channel closed", c) 153 return 154 } 155 sc := c.BestConn() 156 if sc == nil { 157 // maybe this should be a logged error, not a panic. 158 panic("sending out multiconn without any live connection") 159 } 160 161 i++ 162 log.Infof("%s sending (%d)", sc, i) 163 sc.Out() <- m 164 } 165 } 166 } 167 168 // fanInSingle is a multiplexor in -- it receives incoming messages over the 169 // underlying single connections. 170 func (c *MultiConn) fanInSingle(child Conn) { 171 // cleanup all data associated with this child Connection. 172 defer func() { 173 log.Infof("closing: %s", child) 174 175 // in case it still is in the map, remove it. 176 c.Lock() 177 delete(c.conns, child.ID()) 178 connLen := len(c.conns) 179 c.Unlock() 180 181 c.Children().Done() 182 child.Children().Done() 183 184 if connLen == 0 { 185 c.Close() // close self if all underlying children are gone? 186 } 187 }() 188 189 i := 0 190 for { 191 select { 192 case <-c.Closing(): // multiconn closing 193 return 194 195 case <-child.Closing(): // child closing 196 return 197 198 case m, more := <-child.In(): // receiving data 199 if !more { 200 log.Infof("%s in channel closed", child) 201 err := c.GetError() 202 if err != nil { 203 log.Errorf("Found error on connection: %s", err) 204 } 205 return // closed 206 } 207 i++ 208 log.Infof("%s received (%d)", child, i) 209 c.duplex.In <- m 210 } 211 } 212 } 213 214 // close is the internal close function, called by ContextCloser.Close 215 func (c *MultiConn) close() error { 216 log.Debugf("%s closing Conn with %s", c.local, c.remote) 217 218 // get connections 219 c.RLock() 220 conns := make([]Conn, 0, len(c.conns)) 221 for _, c := range c.conns { 222 conns = append(conns, c) 223 } 224 c.RUnlock() 225 226 // close underlying connections 227 CloseConns(conns...) 228 return nil 229 } 230 231 // BestConn is the best connection in this MultiConn 232 func (c *MultiConn) BestConn() Conn { 233 c.RLock() 234 defer c.RUnlock() 235 236 var id1 string 237 var c1 Conn 238 for id2, c2 := range c.conns { 239 if id1 == "" || id1 < id2 { 240 id1 = id2 241 c1 = c2 242 } 243 } 244 return c1 245 } 246 247 // ID is an identifier unique to this connection. 248 // In MultiConn, this is all the children IDs XORed together. 249 func (c *MultiConn) ID() string { 250 c.RLock() 251 defer c.RUnlock() 252 253 ids := []byte(nil) 254 for i := range c.conns { 255 if ids == nil { 256 ids = []byte(i) 257 } else { 258 ids = u.XOR(ids, []byte(i)) 259 } 260 } 261 262 return string(ids) 263 } 264 265 func (c *MultiConn) String() string { 266 return String(c, "MultiConn") 267 } 268 269 // LocalMultiaddr is the Multiaddr on this side 270 func (c *MultiConn) LocalMultiaddr() ma.Multiaddr { 271 return c.BestConn().LocalMultiaddr() 272 } 273 274 // RemoteMultiaddr is the Multiaddr on the remote side 275 func (c *MultiConn) RemoteMultiaddr() ma.Multiaddr { 276 return c.BestConn().RemoteMultiaddr() 277 } 278 279 // LocalPeer is the Peer on this side 280 func (c *MultiConn) LocalPeer() peer.Peer { 281 return c.local 282 } 283 284 // RemotePeer is the Peer on the remote side 285 func (c *MultiConn) RemotePeer() peer.Peer { 286 return c.remote 287 } 288 289 // In returns a readable message channel 290 func (c *MultiConn) In() <-chan []byte { 291 return c.duplex.In 292 } 293 294 // Out returns a writable message channel 295 func (c *MultiConn) Out() chan<- []byte { 296 return c.duplex.Out 297 } 298 299 func (c *MultiConn) GetError() error { 300 c.RLock() 301 defer c.RUnlock() 302 for _, sub := range c.conns { 303 err := sub.GetError() 304 if err != nil { 305 return err 306 } 307 } 308 return nil 309 }