// Send a line to the node, once we have a synchronized connection to it. // If the node is unreachable, we drop the line. // Must be run from the node's goroutine. func (n *Node) sendSyncLine(line *mmn.Line) { // If this node is ourselves, send it directly to our receive chan. if n == Me { n.receive <- line return } // If we have a synchronized connection now, send it now. if n.conn != nil && n.conn.State == connect.ConnStateNormal { err := n.conn.WriteLine(line) if err != nil { n.conn.Close() } return } // Otherwise, add it to the queue. n.queue = append(n.queue, line) // If we have no connection, make an attempt to establish one. // Drop the line if we get an error. if n.conn == nil { var err error n.conn, err = connect.NewOutgoing(n.ConnInfo) if err != nil { n.queue = n.queue[:0] } } }
// The node's goroutine. Handle lines sent to or received from this node. // TODO: Figure out how not to deadlock when two nodes send to each other. // Use an intermediary? func (n *Node) process() { for { select { // Handle a received line on our connection. case line, ok := <-n.receive: // Check for connection closed. if ok { // Process the line. n.receiveLine(line) continue } n.receive = nil // Stop us selecting on closed chan. // If we have a waiting incoming connection, take that. if n.waiting != nil { n.conn = connect.NewIncoming(n.waiting) n.waiting = nil n.receive = make(chan *mmn.Line, 10) go n.conn.ReadLines(n.receive) // Send initial connection message. line := connect.MakeVersionList() n.conn.WriteLine(line) continue } // Otherwise, try to make a new connection. var err error n.conn, err = connect.NewOutgoing(n.ConnInfo) if err == nil { n.receive = make(chan *mmn.Line, 10) go n.conn.ReadLines(n.receive) } // Handle a line to be sent on our connection. case line := <-n.send: // Send the line. n.sendSyncLine(line) // Handle a new connection from this node. case conn := <-n.NewConn: // If we have no existing connection, adopt this one. if n.conn == nil { n.conn = connect.NewIncoming(conn) n.receive = make(chan *mmn.Line, 10) go n.conn.ReadLines(n.receive) // Send initial connection message. line := connect.MakeVersionList() n.conn.WriteLine(line) continue } // If our connection's state is not a new outgoing, // close it, wait for reading to end, then adopt // this as our connection. if n.conn.State != connect.ConnStateInitialOutgoing { n.conn.Close() n.waiting = conn continue } // TODO: What to do if it is? // Two outgoing connections crossing. // For now, close it; two connections exactly at once // can fail, but hopefully a retry will deal with it. conn.Close() // Asks the node to attempt to make a connection. // Only does anything if it doesn't currently have one. case <-n.connect: // If they already have a connection, skip. if n.conn != nil { continue } // Otherwise, attempt an outgoing connection. var err error n.conn, err = connect.NewOutgoing(n.ConnInfo) if err == nil { n.receive = make(chan *mmn.Line, 10) go n.conn.ReadLines(n.receive) } } } }