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  }